Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
103 lines (86 sloc) 3.09 KB
(ns
^{:author "Miki Tebeka <miki.tebeka@gmail.com>"
:doc "Message digest algorithms for Clojure"}
digest
(:require [clojure.string :refer [join lower-case split]])
(:import (java.io File FileInputStream InputStream)
(java.security MessageDigest Provider Security)
(java.util Arrays)))
; Default buffer size for reading
(def ^:dynamic *buffer-size* 1024)
(defn- read-some
"Read some data from reader. Return [data size] if there's more to read,
otherwise nil."
[^InputStream reader]
(let [^bytes buffer (make-array Byte/TYPE *buffer-size*)
size (.read reader buffer)]
(when (pos? size)
(if (= size *buffer-size*) buffer (Arrays/copyOf buffer size)))))
(defn- byte-seq
"Return a sequence of [data size] from reader."
[^InputStream reader]
(take-while some? (repeatedly (partial read-some reader))))
(defn- signature
"Get signature (string) of digest."
[^MessageDigest algorithm]
(let [size (* 2 (.getDigestLength algorithm))
sig (.toString (BigInteger. 1 (.digest algorithm)) 16)
padding (join (repeat (- size (count sig)) "0"))]
(str padding sig)))
(defprotocol Digestible
(-digest [message algorithm]))
(extend-protocol Digestible
(class (make-array Byte/TYPE 0))
(-digest [message algorithm]
(-digest [message] algorithm))
java.util.Collection
;; Code "borrowed" from
;; * http://www.holygoat.co.uk/blog/entry/2009-03-26-1
;; * http://www.rgagnon.com/javadetails/java-0416.html
(-digest [message algorithm]
(let [^MessageDigest algo (MessageDigest/getInstance algorithm)]
(.reset algo)
(doseq [^bytes b message] (.update algo b))
(signature algo)))
String
(-digest [message algorithm]
(-digest [(.getBytes message)] algorithm))
InputStream
(-digest [reader algorithm]
(-digest (byte-seq reader) algorithm))
File
(-digest [file algorithm]
(with-open [f (FileInputStream. file)]
(-digest f algorithm)))
nil
(-digest [message algorithm]
nil))
(defn digest
"Returns digest for message with given algorithm."
[algorithm message]
(-digest message algorithm))
(defn algorithms
"List support digest algorithms."
[]
(let [providers (vec (Security/getProviders))
names (mapcat (fn [^Provider p] (enumeration-seq (.keys p))) providers)
digest-names (filter #(re-find #"MessageDigest\.[A-Z0-9-]+$" %) names)]
(set (map #(last (split % #"\.")) digest-names))))
(defn create-fn!
[algorithm-name]
(let [update-meta (fn [meta]
(assoc meta
:doc (str "Encode the given message with the " algorithm-name " algorithm.")
:arglists '([message])))]
(-> (intern 'digest
(symbol (lower-case algorithm-name))
(partial digest algorithm-name))
(alter-meta! update-meta))))
(defn- create-fns
"Create utility function for each digest algorithms.
For example will create an md5 function for MD5 algorithm."
[]
(doseq [algorithm (algorithms)]
(create-fn! algorithm)))
; Create utility functions such as md5, sha-256 ...
(create-fns)
You can’t perform that action at this time.