diff --git a/README.md b/README.md index 64825bf..0af6906 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A Clojure library for securing user passwords using a * [PBKDF2](http://en.wikipedia.org/wiki/PBKDF2) * [Bcrypt](http://bcrypt.sourceforge.net/) * [scrypt](http://www.tarsnap.com/scrypt.html) +* [argon2](https://github.com/phxql/argon2-jvm) [1]: http://en.wikipedia.org/wiki/Key_derivation_function @@ -19,7 +20,8 @@ Add the following dependency to your `project.clj` file: ## Usage -Pick an encryption algorithm, either `pbkdf2`, `bcrypt` or `scrypt`: +Pick an encryption algorithm, either `pbkdf2`, `bcrypt`, `scrypt` +or `argon2`: ```clojure (require '[crypto.password. :as password]) diff --git a/project.clj b/project.clj index d54f032..a9a3c26 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,8 @@ [crypto-equality "1.0.0"] [commons-codec "1.15"] [at.favre.lib/bcrypt "0.9.0"] - [com.lambdaworks/scrypt "1.4.0"]] + [com.lambdaworks/scrypt "1.4.0"] + [de.mkammerer/argon2-jvm "2.11"]] :plugins [[lein-codox "0.9.4"]] :codox {:output-path "codox" diff --git a/src/crypto/password/argon2.clj b/src/crypto/password/argon2.clj new file mode 100644 index 0000000..27a4ace --- /dev/null +++ b/src/crypto/password/argon2.clj @@ -0,0 +1,38 @@ +(ns crypto.password.argon2 + "Functions for encrypting passwords using the recommended argon2 algorithm. + + See: https://infosecscout.com/best-algorithm-password-storage + https://github.com/phxql/argon2-jvm" + (:import [de.mkammerer.argon2 Argon2 Argon2Factory Argon2Advanced])) + +(def argon2 (Argon2Factory/create)) + +(def ^:private default-iterations + (Long/parseLong (System/getProperty "crypto.password.argon2.default-iterations" "22"))) + +(def ^:private default-memory-cost + (Long/parseLong (System/getProperty "crypto.password.argon2.default-memory-cost" "65536"))) + +(def ^:private default-parallelization-parameter + (Long/parseLong (System/getProperty "crypto.password.argon2.default-parallelization-parameter" "1"))) + +(defn encrypt + "Encrypt a password string using the argon2 algorithm. This function takes + three optional parameters: + + * `iter` - the number of iterations, defaults to 22 + * `mem` - the memory cost, defaults to 65536 + * `parallel` - the parallelization parameter, defaults to 1" + ([raw] + (encrypt raw + default-iterations + default-memory-cost + default-parallelization-parameter)) + ([raw iter mem parallel] + (.hash argon2 iter mem parallel raw))) + +(defn check + "Compare a raw string with a string encrypted with the [[encrypt]] + function. Returns true if the string matches, false otherwise." + [raw hash] + (.verify argon2 hash raw)) diff --git a/test/crypto/password/argon2_test.clj b/test/crypto/password/argon2_test.clj new file mode 100644 index 0000000..899fc4e --- /dev/null +++ b/test/crypto/password/argon2_test.clj @@ -0,0 +1,20 @@ +(ns crypto.password.argon2-test + (:require [clojure.test :refer [deftest are]] + [crypto.password.argon2 :as password])) + +(deftest test-passwords + (are [s] (password/check s (password/encrypt 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 (password/check r (password/encrypt s))) + "a" "b" + "a" "a " + "aaaaa" "aaaaa\n" + "großpösna" "grossposna"))