Class: CryptCheckpass::Scrypt

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

Overview

This class is to support RFC7914-related hash variants.

Format:

Life gets extremely hard here because Ruby's scrypt gem does not follow the Modular Crypt Format. You cannot tell if a string is scrypt-generated or not by looking at its beginning.

%r{
  (?<N>    [0-9a-f]+ ){0}
  (?<r>    [0-9a-f]+ ){0}
  (?<p>    [0-9a-f]+ ){0}
  (?<salt> [0-9a-f]+ ){0}
  (?<csum> [0-9a-f]+ ){0}

  \A     \g<N>
     [$] \g<r>
     [$] \g<p>
     [$] \g<salt>
     [$] \g<csum>
  \z
}x
  • N is the CPU/Memory cost parameter N ("costParameter").

  • r is the block size parameter r ("blockSize").

  • p is the parallelization parameter p ("parallelizationParameter").

  • salt is the salt string.

  • csum is the checksum string.

All of above fields are represented as hexadecimal numbers.

This is too different from other password hashs. To ease the situation we also follow extra format that is compatible with Python's Passlib and npm's @phc/scrypt generates.

%r{
  (?<id>   scrypt         ){0}
  (?<ln>   ln=[1-9][0-9]* ){0}
  (?<r>    r=[1-9][0-9]*  ){0}
  (?<p>    p=[1-9][0-9]*  ){0}
  (?<salt> [a-zA-Z0-9+/]* ){0}
  (?<csum> [a-zA-Z0-9+/]* ){0}

  \A [$] \g<id>
     [$] \g<ln>
     [,] \g<r>
     [,] \g<p>
     [$] \g<salt>
     [$] \g<csum>
  \z
}x
  • Parameters are ln, r, and p where ln deontes log2(N).

Other formats:

Seems there are no such thing like a standard way to encode scrypt-generated passwords. Lots of wild formats are seen. We could support them if they have actual usage and to be migrated to another format.

Examples:

crypt_newhash 'password', id: 'scrypt'
# => "$scrypt$ln=8,r=8,p=1$aL2uvFKrfoVkxAgy1j/Y4OAJ8D0p1yP/uqFg3UU8t64$/xZGQyALLQrKzaBRGwzGCw+FGgRqFwyCfZddC5qvZYA"
crypt_checkpass? 'password', '$scrypt$ln=8,r=8,p=1$aL2uvFKrfoVkxAgy1j/Y4OAJ8D0p1yP/uqFg3UU8t64$/xZGQyALLQrKzaBRGwzGCw+FGgRqFwyCfZddC5qvZYA'
# => 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.



124
125
126
127
128
129
130
131
# File 'lib/crypt_checkpass/scrypt.rb', line 124

def self.checkpass? pass, hash
  require 'scrypt'

  case hash
  when /\A\$scrypt\$/ then return checkpass_phc pass, hash
  else                     return checkpass_gem pass, hash
  end
end

.newhash(pass, id: 'scrypt', ln: 8, r: 8, p: 1) ⇒ 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: 'scrypt')

    name of the algorithm (ignored)

  • ln (Integer) (defaults to: 8)

    cost parameter in log2.

  • r (Integer) (defaults to: 8)

    block size.

  • p (Integer) (defaults to: 1)

    parallelism parameter.

Returns:

  • (String)

    hashed digest string of password.



145
146
147
148
149
150
151
152
# File 'lib/crypt_checkpass/scrypt.rb', line 145

def self.newhash pass, id: 'scrypt', ln: 8, r: 8, p: 1
  require 'scrypt'

  salt = SecureRandom.random_bytes ::SCrypt::Engine::DEFAULTS[:salt_size]
  klen = ::SCrypt::Engine::DEFAULTS[:key_len]
  csum = ::SCrypt::Engine.scrypt pass, salt, 2 ** ln, r, p, klen
  return phcencode 'scrypt', { ln: ln, r: r, p: p }, salt, csum
end

.provide?(id) ⇒ true, false

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.



134
135
136
# File 'lib/crypt_checkpass/scrypt.rb', line 134

def self.provide? id
  return id == 'scrypt'
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.



119
120
121
# File 'lib/crypt_checkpass/scrypt.rb', line 119

def self.understand? str
  return match? str, understander
end