Class: CryptCheckpass::Argon2

Inherits:
CryptCheckpass show all
Defined in:
lib/crypt_checkpass/argon2.rb

Overview

Argon2 the Password Hashing Competition winner.

Newhash:

You can use crypto_newhash to create a new password hash using argon2:

crypt_newhash(password, id: 'argon2i', m_cost: 12, t_cost: 3)

where:

- `password` is the raw binary password that you want to digest.

- `id`  is "argon2i"  when  you want  an argon2  hash.   Due to  underlying
ruby-argon2 gem's restriction we do not support other argon2 variants.

- `m_cost` and `t_cost` are both integer parameter to the algorithm.

The generated password hash has following format.

Format:

Argon2 specifies its hash structure in detail named "PHC String Format", then ignored the format by itself. See also 1. Empirical findings show that the algorithm now has the following output format:

%r{
  (?<digits> 0|[1-9]\d*      ){0}
  (?<b64>    [a-zA-Z0-9+/]   ){0}
  (?<id>     argon2 (i|d|id) ){0}
  (?<v>      v=19            ){0}
  (?<m>      m=\g<digits>    ){0}
  (?<t>      t=\g<digits>    ){0}
  (?<p>      p=\g<digits>    ){0}
  (?<salt>   \g<b64>+        ){0}
  (?<csum>   \g<b64>+        ){0}

  \A     [$] \g<id>
     (?: [$] \g<v> )?
         [$] \g<m>
         [,] \g<t>
         [,] \g<p>
         [$] \g<salt>
         [$] \g<csum>
  \z
}x
- `id`  is   "argon2"  +  something   that  denotes  the  variant   of  the
hash. Variant "argon2i" seems most widely adopted.

- `v` is, when  available, a number 19.  That doesn't  mean anything.  What
is important is the _absence_ of that parameter, which means the hash was
generated using old argon2 1.0 and shall be out of date.

- `m` is the  amount of memory filled by the  algorithm (2**m KiB).  Memory
consumption depends on this parameter.

- `t` is  the number of passes  over the memory.  The  running time depends
linearly on this parameter.

- `p` is the degree of parallelism, called "lanes" in the C implementation.

- `salt` and `csum` are the salt and checksum strings.  Both are encoded in
base64-like strings that  do not strictly follow RFC4648.   They both can
be arbitrary length.  In case there are "unused" bits at the end of those
fields, they shall be zero-filled.

Implementation limitations:

Ruby binding of argon2 library (ruby-argon2) is pretty well designed and can be recommended for daily uses. You really should use it whenever possible. The big problem is however, that it only supports argon2i. That is definitely OK for hash generation. However in verifying, it is desirable to support other variants.

In order to reroute this problem we load the ruby-argon2 gem, then ignore its ruby part and directly call the canonical C implementation via FFI.

Examples:

crypt_newhash 'password', id: 'argon2i'
# => "$argon2i$v=19$m=4096,t=3,p=1$b9AqucWUJADOdNMW8fW+0A$s3+Yno9+X7rpA2AsaG7KnoBtjQiE+AUevLvT7u1lXeA"
crypt_checkpass? 'password', '$argon2i$v=19$m=4096,t=3,p=1$b9AqucWUJADOdNMW8fW+0A$s3+Yno9+X7rpA2AsaG7KnoBtjQiE+AUevLvT7u1lXeA'
# => true

See Also:

Class Method Summary collapse

Methods inherited from CryptCheckpass

crypt_checkpass?, crypt_newhash

Class Method Details

.checkpass?(pass, hash) ⇒ true, false

Checks if the given password matches the hash.

Parameters:

  • pass (String) —

    a password to test.

  • hash (String) —

    a good hash digest string.

Returns:

  • (true) —

    they are identical.

  • (false) —

    they are distinct.

Raises:

  • (NotImplementedError) —

    don't know how to parse hash.



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/crypt_checkpass/argon2.rb', line 144

def self.checkpass? pass, hash
  h = hash
  p = pass
  n = pass.bytesize

  __load_argon2_dll

  case hash
  when /\A\$argon2i\$/  then ret = @dll.argon2i_verify  h, p, n
  when /\A\$argon2d\$/  then ret = @dll.argon2d_verify  h, p, n
  when /\A\$argon2id\$/ then ret = @dll.argon2id_verify h, p, n
  else raise ArgumentError, "unknown hash format %p", hash
  end

  case ret
  when 0   then return true
  when -35 then return false # ARGON2_VERIFY_MISMATCH
  else
    errstr = ::Argon2::ERRORS[ret.abs] || ret.to_s
    raise ::Argon2::ArgonHashFail, "got %s", errstr
  end
end

.newhash(pass, id: 'argon2i', m_cost: 12, t_cost: 3) ⇒ String

Note:

There is no way to specify salt. That's a bad idea.

Generate a new password hash string.

Parameters:

  • pass (String) —

    raw binary password string.

  • id (String) (defaults to: 'argon2i') —

    name of the algorithm (ignored)

  • m_cost (Integer) (defaults to: 12) —

    argon2 memory usage (2^m KiB)

  • t_cost (Integer) (defaults to: 3) —

    argon2 iterations.

Returns:

  • (String) —

    hashed digest string of password.



179
180
181
182
183
184
# File 'lib/crypt_checkpass/argon2.rb', line 179

def self.newhash pass, id: 'argon2i', m_cost: 12, t_cost: 3
  require 'argon2'

  argon2 = ::Argon2::Password.new m_cost: m_cost, t_cost: t_cost
  return argon2.create pass
end

.provide?(id) ⇒ true, false

Note:

we don't support generating argon2d hashs.

Checks if the given ID can be handled by this class. A class is free to handle several IDs, like 'argon2i', 'argon2d', ...

Parameters:

  • id (String) —

    hash function ID.

Returns:

  • (true) —

    it does.

  • (false) —

    it desn't.



169
170
171
# File 'lib/crypt_checkpass/argon2.rb', line 169

def self.provide? id
  return id == 'argon2i'
end

.understand?(str) ⇒ true, false

Checks if the given hash string can be handled by this class.

Parameters:

  • str (String) —

    a good hashed string.

Returns:

  • (true) —

    it does.

  • (false) —

    it desn't.



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/crypt_checkpass/argon2.rb', line 120

def self.understand? str
  return match? str, %r{
    (?<id>     argon2 (i|d|id) ){0}
    (?<digits> 0|[1-9]\d*      ){0}
    (?<b64>    [a-zA-Z0-9+/]   ){0}
    (?<v>      v=19            ){0}
    (?<m>      m=\g<digits>    ){0}
    (?<t>      t=\g<digits>    ){0}
    (?<p>      p=\g<digits>    ){0}
    (?<salt>   \g<b64>+        ){0}
    (?<csum>   \g<b64>+        ){0}

    \A     [$] \g<id>
       (?: [$] \g<v> )?
           [$] \g<m>
           [,] \g<t>
           [,] \g<p>
           [$] \g<salt>
           [$] \g<csum>
    \z
  }x
end