|
| 1 | +(ns |
| 2 | + ^{ :author "Miki Tebeka <[email protected]>" |
| 3 | + :doc "Message digest algorithms for Clojure"} |
| 4 | + clj-commons.digest |
| 5 | + (:require [clojure.string :refer [join lower-case split]]) |
| 6 | + (:import (java.io File FileInputStream InputStream) |
| 7 | + (java.security MessageDigest Provider Security) |
| 8 | + (java.util Arrays))) |
| 9 | + |
| 10 | +; Default buffer size for reading |
| 11 | +(def ^:dynamic *buffer-size* 1024) |
| 12 | + |
| 13 | +(defn- read-some |
| 14 | + "Read some data from reader. Return [data size] if there's more to read, |
| 15 | + otherwise nil." |
| 16 | + [^InputStream reader] |
| 17 | + (let [^bytes buffer (make-array Byte/TYPE *buffer-size*) |
| 18 | + size (.read reader buffer)] |
| 19 | + (when (pos? size) |
| 20 | + (if (= size *buffer-size*) buffer (Arrays/copyOf buffer size))))) |
| 21 | + |
| 22 | +(defn- byte-seq |
| 23 | + "Return a sequence of [data size] from reader." |
| 24 | + [^InputStream reader] |
| 25 | + (take-while some? (repeatedly (partial read-some reader)))) |
| 26 | + |
| 27 | +(defn- signature |
| 28 | + "Get signature (string) of digest." |
| 29 | + [^MessageDigest algorithm] |
| 30 | + (let [size (* 2 (.getDigestLength algorithm)) |
| 31 | + sig (.toString (BigInteger. 1 (.digest algorithm)) 16) |
| 32 | + padding (join (repeat (- size (count sig)) "0"))] |
| 33 | + (str padding sig))) |
| 34 | + |
| 35 | +(defprotocol Digestible |
| 36 | + (-digest [message algorithm])) |
| 37 | + |
| 38 | +(extend-protocol Digestible |
| 39 | + (class (make-array Byte/TYPE 0)) |
| 40 | + (-digest [message algorithm] |
| 41 | + (-digest [message] algorithm)) |
| 42 | + |
| 43 | + java.util.Collection |
| 44 | + ;; Code "borrowed" from |
| 45 | + ;; * http://www.holygoat.co.uk/blog/entry/2009-03-26-1 |
| 46 | + ;; * http://www.rgagnon.com/javadetails/java-0416.html |
| 47 | + (-digest [message algorithm] |
| 48 | + (let [^MessageDigest algo (MessageDigest/getInstance algorithm)] |
| 49 | + (.reset algo) |
| 50 | + (doseq [^bytes b message] (.update algo b)) |
| 51 | + (signature algo))) |
| 52 | + |
| 53 | + String |
| 54 | + (-digest [message algorithm] |
| 55 | + (-digest [(.getBytes message)] algorithm)) |
| 56 | + |
| 57 | + InputStream |
| 58 | + (-digest [reader algorithm] |
| 59 | + (-digest (byte-seq reader) algorithm)) |
| 60 | + |
| 61 | + File |
| 62 | + (-digest [file algorithm] |
| 63 | + (with-open [f (FileInputStream. file)] |
| 64 | + (-digest f algorithm))) |
| 65 | + |
| 66 | + nil |
| 67 | + (-digest [message algorithm] |
| 68 | + nil)) |
| 69 | + |
| 70 | +(defn digest |
| 71 | + "Returns digest for message with given algorithm." |
| 72 | + [algorithm message] |
| 73 | + (-digest message algorithm)) |
| 74 | + |
| 75 | +(defn algorithms |
| 76 | + "List supported digest algorithms." |
| 77 | + [] |
| 78 | + (let [providers (vec (Security/getProviders)) |
| 79 | + names (mapcat (fn [^Provider p] (enumeration-seq (.keys p))) providers) |
| 80 | + digest-names (filter #(re-find #"MessageDigest\.[A-Z0-9-]+$" %) names)] |
| 81 | + (set (map #(last (split % #"\.")) digest-names)))) |
| 82 | + |
| 83 | +(defn- create-fn! |
| 84 | + [algorithm-name] |
| 85 | + (let [update-meta (fn [meta] |
| 86 | + (assoc meta |
| 87 | + :doc (str "Encode the given message with the " algorithm-name " algorithm.") |
| 88 | + :arglists '([message])))] |
| 89 | + (-> (intern *ns* |
| 90 | + (symbol (lower-case algorithm-name)) |
| 91 | + (partial digest algorithm-name)) |
| 92 | + (alter-meta! update-meta)))) |
| 93 | + |
| 94 | +(defn- create-fns |
| 95 | + "Create utility function for each digest algorithms. |
| 96 | + For example will create an md5 function for MD5 algorithm." |
| 97 | + [] |
| 98 | + (doseq [algorithm (algorithms)] |
| 99 | + (create-fn! algorithm))) |
| 100 | + |
| 101 | +; Create utility functions such as md5, sha-256 ... |
| 102 | +(create-fns) |
| 103 | + |
| 104 | +;;;; Hints for clj-kondo |
| 105 | + |
| 106 | +(comment |
| 107 | + (declare sha3-384 sha-256 sha3-256 sha-384 sha3-512 sha-1 sha-224 sha1 sha-512 md2 sha sha3-224 md5) |
| 108 | + ) |
0 commit comments