From 2a0293cfc25b0c9de3829dbf92ef8175550e4ff7 Mon Sep 17 00:00:00 2001 From: Baishampayan Ghose Date: Wed, 26 Mar 2014 11:50:02 +0530 Subject: [PATCH 1/2] Increase default iterations of PBKDF2 to 100,000. 20,000 is too low to be a sane default in 2014. According to OWASP the rule of thumb is to tune the work-factor to make sure the process takes around 250ms to 1s. 100,000 iterations takes around 241ms on my mid-2012 MacBook Air, so given modern hardware 100,000 seems to be a fairly sane default. --- src/crypto/password/pbkdf2.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/crypto/password/pbkdf2.clj b/src/crypto/password/pbkdf2.clj index 951a7dd..ca8b668 100644 --- a/src/crypto/password/pbkdf2.clj +++ b/src/crypto/password/pbkdf2.clj @@ -26,7 +26,7 @@ All elements in the output string are Base64 encoded." ([raw] - (encrypt raw 20000)) + (encrypt raw 100000)) ([raw iterations] (encrypt raw iterations (random/bytes 8))) ([raw iterations salt] From db64f089b7b5819feee9ee1daa468bb479fc8c7a Mon Sep 17 00:00:00 2001 From: Baishampayan Ghose Date: Wed, 26 Mar 2014 16:03:42 +0530 Subject: [PATCH 2/2] Add support for HMAC. Supported algorithms: HMACMD5, HMACSHA1, HMACSHA256 (default), HMACSHA384 & HMACSHA512. --- src/crypto/password/hmac.clj | 47 ++++++++++++++++++++++++++++++ test/crypto/password/test/hmac.clj | 27 +++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/crypto/password/hmac.clj create mode 100644 test/crypto/password/test/hmac.clj diff --git a/src/crypto/password/hmac.clj b/src/crypto/password/hmac.clj new file mode 100644 index 0000000..eb63fee --- /dev/null +++ b/src/crypto/password/hmac.clj @@ -0,0 +1,47 @@ +(ns crypto.password.hmac + "Functions for calculating HMAC using multiple crypto hash functions." + (:refer-clojure :exclude [bytes hash]) + (:require [crypto.equality :as crypto]) + (:import javax.crypto.Mac + javax.crypto.spec.SecretKeySpec + org.apache.commons.codec.binary.Hex)) + +(def ^{:private true :doc "Standard crypto hash functions for MAC."} + hash {:hmacmd5 "HMACMD5" + :hmacsha1 "HMACSHA1" + :hmacsha256 "HMACSHA256" + :hmacsha384 "HMACSHA384" + :hmacsha512 "HMACSHA512"}) + + +(defn- bytes + "Convert a String to a byte-array." + [^String s] + (.getBytes s "UTF-8")) + + +(defn hmac + "Compute HMAC of `msg` given secret key `k`. + Optionally pass in the algorithm to be used for computing HMAC (defaults to :hmacsha1). + Valid options are :hmacmd5, :hmacsha1, :hmacsha256, :hmac384 & :hmacsha512." + ([k msg] + (hmac k msg :hmacsha1)) + ([k msg algo] + (if-let [algo* (hash algo)] + (let [key-spec (SecretKeySpec. (bytes k) algo*) + mac (.doFinal (doto (Mac/getInstance algo*) (.init key-spec)) + (bytes msg))] + (Hex/encodeHexString mac)) + (throw (IllegalArgumentException. + (format "Incorrect hash algorithm %s specified. Valid choices are %s." + algo (keys hash))))))) + + +(defn check + "Check if the given HMAC is correct given the key & algorithm. + Optionally pass in the algorithm to be used for computing HMAC (defaults to :hmacsha1). + Valid options are :hmacmd5, :hmacsha1, :hmacsha256, :hmac384 & :hmacsha512." + ([mac k original] + (check mac k original :hmacsha1)) + ([mac k original algo] + (crypto/eq? mac (hmac k original algo)))) diff --git a/test/crypto/password/test/hmac.clj b/test/crypto/password/test/hmac.clj new file mode 100644 index 0000000..c6df70a --- /dev/null +++ b/test/crypto/password/test/hmac.clj @@ -0,0 +1,27 @@ +(ns crypto.password.test.hmac + (:use clojure.test) + (:require [crypto.password.hmac :as mac])) + +(deftest test-hmac + (are [algo result] (= (mac/hmac "key" "The quick brown fox jumps over the lazy dog" algo) result) + :hmacmd5 "80070713463e7749b90c2dc24911e275" + :hmacsha1 "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9" + :hmacsha256 "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8" + :hmacsha384 "d7f4727e2c0b39ae0f1e40cc96f60242d5b7801841cea6fc592c5d3e1ae50700582a96cf35e1e554995fe4e03381c237" + :hmacsha512 "b42af09057bac1e2d41708e48a902e09b5ff7f12ab428a4fe86653c73dd248fb82f948a549f7b791a5b41915ee4d1ec3935357e4e2317250d0372afa2ebeeb3a") + + (are [s] (mac/check (mac/hmac "foobar" s) "foobar" s) + "a" + "foo" + "password" + "Testing" + "Test123" + "ÁäñßOÔ" + "großpösna" + "Some rather long pass phrase perhaps out of a book or poem") + + (are [s r] (not (mac/check (mac/hmac "foobar" s) "foobar" r)) + "a" "b" + "a" "a " + "aaaaa" "aaaaa\n" + "großpösna" "grossposna"))