- PBKDF2
- BCRYPT
- SCRYPT
- ARGON2
- stealth-mode (optional zero-ing of arrays passed as input or transiently created)
- API for dealing with
char
/byte
arrays as input (required for stealth-mode)
- Support for overly long values (> 72 bytes) in BCrypt (truncate/SHA512)
- Highly configurable (with modern/safe defaults)
- Fully spec-ed (but not enforced)
- Reflection-free (despite the heavy interop)
- Single dependency
Because Clojure deserves the best crypto-hashers ;)
There are two ways to leverage cryptohash-clj
.
This namespace contains two multi-methods:
hash-with [algo raw options]
verify-with [algo raw options hashed]
These will delegate to the right implementation according to the first parameter (:pbkdf2
, :bcrypt
, :scrypt
). For example:
(hash-with :pbkdf2 "_sUpErSeCrEt@1234!_" {:algo :hmac+sha256})
=> "250000$256$hmac+sha256$zv6x/KfjV1318e3kF1aWBQ$qXoIRxVrtVbMnABxh5eTbDfszyc/O4uZIV8QPsLkyhE"
(verify-with :pbkdf2 "_sUpErSeCrEt@1234!_" *1)
=> true
(hash-with :bcrypt "_sUpErSeCrEt@1234!_" {:cpu-cost 8})
=> "$2y$08$0mddgmeilGorJlk.t4JYXu/b/K7S4VOD9LkddsPGyyrJp0TsEaOIu"
(verify-with :bcrypt "_sUpErSeCrEt@1234!_" *1)
=> true
Note that all the supported algorithms produce values that include the params in them so strictly speaking there shouldn't be a need
for passing any options to verify-with
. However, there are some exceptions - for instance in pbkdf2 you can specify a custom separator
(subject to validation). If you choose to do so, it needs to be known when verifying. Similarly with BCrypt and its long-value
parameter. For piece of mind you can always rely on the defaults (for those parameters) which will allow you to verify w/o passing options.
If you don't want to go via the multi-methods, you can go via the individual implementation namespaces.
Each of the three namespaces (bcrypt.clj
, scrypt.clj
, pbkdf2.clj
, argon2.clj
) contains two public functions:
chash ([raw opts] [raw])
verify ([raw opts hashed] [raw hashed])
Can be configured with the following options:
:separator
(defaults to\$
):iterations
(defaults to250,000
):algo
(defaults to:hmac+sha512
, but:hmac+sha256
and:hmac+sha1
are valid choices):salt-length
(defaults to16
bytes):key-length
(defaults to the native output length of:algo
- 64, 32 and 20 bytes respectively)
I would advise against overriding the default key-length
.
You should certainly avoid providing a number of bits (bytes * 8) greater than the native output length of your chosen algo
as it makes life easier for an attacker. Providing less is safer, but since it won't save you any computation, it's
best to stick with the native output length.
Can be configured with the following options:
:version
(defaults to:v2y
but:v2a
and:v2b
are valid choices):cpu-cost
(defaults to14
):long-value
(defaults to:sha512
, but:truncate
is a valid choice)
Can be configured with the following options:
:salt-length
(defaults to16
bytes):cpu-cost
(defaults to17
):mem-cost
(defaults to8
):pfactor
(parallelization factor - defaults to1
)
Can be configured with the following options:
:type
(defaults to:argon2id
):version
(defaults to:v13
):key-length
(defaults to32
bytes):salt-length
(defaults to16
bytes):secret
(bytes of some secret):additional
(additional bytes to include):iterations
(defaults to100
):mem-cost
(defaults to12
)
Stealth mode is controlled by cryptohash-clj.stealth/*stealth?*
(bound to true
).
A convenience macro with-stealth
is also provided in the same namespace for easy overriding.
All random bytes are produced via a global instance of SecureRandom
which lives in cryptohash-clj.random/*PRNG*
.
A convenience macro with-PRNG
is also provided in the same namespace for easy overriding.
Equality comparison is performed in a way that resists timing attacks (see cryptohash-clj.equality
).
The length of the data being compared is still discoverable by an attacker, but in the context of
cryptographic hashing this is not a concern. In fact, many crypto-hashing functions produce fixed/well-known key lengths.
This will, of course, vary from CPU to CPU, but all the defaults have been tuned to produce a time cost of around 400-500 ms, on this (relatively modern) MacBook-Pro (2.8-3.8GHz Intel Core i7) from 2017.
- some recent version of Clojure
crypto-password is the obvious alternative here. However it lacks an api for bytes/chars (even if the underlying Java lib supports it), stealth-mode, and generally speaking is less configurable. Moreover, it comes with several dependencies.
Copyright © 2019 Dimitrios Piliouras
This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which is available at http://www.eclipse.org/legal/epl-2.0.
This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version, with the GNU Classpath Exception which is available at https://www.gnu.org/software/classpath/license.html.