Provides Unix crypt_checkpass(3)-compatible routines.

What's good with this API?

  • It supports vast majority of hash verifications, including those which are not generated by us (see below). This enables smooth migration of password algorithm; you can mix multiple algorithms to verify. This is practically very important because from time to time, current best practice moves from one thing to another.
  • Generates a variety of hash strings using current-best-practice hash functions.

Tell me the usage?

Verification:

require 'crypt_checkpass'
actual   = 'p455w0rd'
expected = '$5$$D1W6NDlv302WsleruXoUN279B87yf6dFN4ZZhFqV6DD'
crypt_checkpass?(actual, expected) or raise 'NG'

Generation:

require 'crypt_checkpass'
crypt_newhash('p455w0rd',
  id: 'argon2i',
  m_cost: 4096,
  t_cost: 3) # => "$argon2i$v=19$m=4096,t=3,p=1$cXdlc..."

Or, if you are a real OpenBSD fan, you can also do this:

require 'crypt_checkpass'
crypt_newhash('p455w0rd', 'bcrypt,12') # => "$2b$12$gCDJbfcg..."

Which hash function is supported?

prefix id supported depends
(none) DES(traditional) NO (1)
_ DES(BSDi extended) NO (2)
$1$ MD5 NO (2)
$2 bcrypt YES bcrypt gem
$3$ BSD NThash NO (2)
$5$ SHA256 YES unix-crypt gem
$6$ SHA512 YES unix-crypt gem
$argon2 argon2 YES argon2 gem
$pbkdf2 PBKFD2 YES openssl gem
(3) scrypt YES scrypt gem
  • note (1): instead of falling back to traditional mode which is considered absolutely insecure, this library prohibits this kind of input and renders errors.
  • note (2): These formats are considered archaic.
  • note (3): scypt gem generates password string that is not prefixed.

Too many functions are there! What is the current best practice?

If in doubt use argon2. It was designed to establish "standard to fulfill the needs of modern applications and to best protect against attackers". As of writing there are no known flaws.

What's wrong with the stdlib String#crypt ?

Please never use String#crypt any longer. It is legacy; provided only for backward compatibility with ruby scripts in earlier days. It is bad to use in contemporary programs for several reasons:

  • Behaviour of C's crypt(3), which is under the hood of String#crypt, depends on the OS it is run. The generated digest lacks data portability.
  • On some OSes such as Mac OS, this function never fails (i.e. silently ends up in unexpected results).
  • On some OSes such as Mac OS, It is not thread safe.
  • So-called "traditional" usage of crypt(3) is very very very weak. According to its manpage, Linux's traditional crypt(3) output only has 2**56 variations; too easy to blute force today. And this is the default behaviour.
  • In order to make things robust some OSes implement so-called "modular" usage. To go through, you have to do a complex build-up of the parameter(salt), by hand. Failure in generation of a proper salt string tends not to yield any errors; for instance typo in parameters are normally not detectable.

    • For instance, in the following example, second invocation of String#crypt is wrong; it has typo in "round=" (lacks "s"). However the call does not fail and something unexpected is generated.
      "foo".crypt("$5$rounds=1000$salt$") # OK, proper usage
      "foo".crypt("$5$round=1000$salt$")  # Typo not detected
    
  • Even in the "modular" mode, some hash functions are considered archaic and no longer recommended at all; for instance module $1$ is officially abandoned by its author: http://phk.freebsd.dk/sagas/md5crypt_eol.html, for another instance module $3$ is considered completely broken: see the manpage of FreeBSD.

  • On some OS such as Mac OS, there is no modular mode. Yet, as written above, String#crypt on Mac OS never fails. This means even if you build up a proper salt string it generates a traditional DES hash anyways, and there is no way for you to be aware of.

  "foo".crypt("$5$rounds=1000$salt$") # => "$5fNPQMxC5j6."

The API this library provides is better. Use ours instead.