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-argons gem's restriction we do not support other argon2 variants.

  • m_cost and t_cost are both integer parameter to the algorighm.

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 absense of that parameter, which means the hash was genrated 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 mumber 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