Class: CryptCheckpass::SHA2

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

Overview

The sha256cyrpt / sha512crypt by Ulrich Drepper. Default for /etc/shadow of most Linux distributions. Also no security flaws are known at the moment.

Newhash:

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

crypt_newhash(password, id: 'sha256', rounds: 1024)

where:

  • password is the raw binary password that you want to digest.

  • id is either "sha256" or "sha512".

  • rounds is for iteration rounds.

The generated password hash has following format.

Format:

Hash strings generated by sha256cyrpt is construted like this:

%r{
  (?<id>     5                   ){0}
  (?<rounds> rounds=[1-9]\d{3,8} ){0}
  (?<salt>   [A-Za-z0-9./]{,16}  ){0}
  (?<csum>   [A-Za-z0-9./]{43}   ){0}

  \A     [$] \g<id>
     (?: [$] \g<rounds> )?
         [$] \g<salt>
         [$] \g<csum>
  \z
}x

That of sha512crypt is construted like this:

%r{
  (?<id>     6                   ){0}
  (?<rounds> rounds=[1-9]\d{3,8} ){0}
  (?<salt>   [A-Za-z0-9./]{,16}  ){0}
  (?<csum>   [A-Za-z0-9./]{86}   ){0}

  \A     [$] \g<id>
     (?: [$] \g<rounds> )?
         [$] \g<salt>
         [$] \g<csum>
  \z
}x
  • id is 5 for sha256cyrpt, 6 for sha512crypt.

  • rounds is, if present, the stretch rounds in decimal integer. Should be in range of 1,000 to 999,999,999 inclusive.

  • salt and csum are the salt and checksum strings. Both are encoded in base64-like strings that do not strictly follow RFC4648. The only difference between $5$ and $6$ is the length of csum.

Examples:

crypt_newhash 'password', id: 'sha256'
# => "$5$eWGIDuRO1LEg8sAB$Pjdxj3AVy4GnFfeOfz8Ek1Gn.vDwTFMMyNk56x/lc.4"
crypt_checkpass? 'password', '$5$eWGIDuRO1LEg8sAB$Pjdxj3AVy4GnFfeOfz8Ek1Gn.vDwTFMMyNk56x/lc.4'
# => true
crypt_newhash 'password', id: 'sha512'
# => "$6$oIlkXbDGlU.HktGx$L7xkRSQYLe/yCbz6hIM2JSY6EMtkr/CyvR71Bhr9VkotfEOUiwY8A0rAuSFmO1titWLA8hTKQXWl3ZX0QqokS0"
crypt_checkpass? 'password', '$6$oIlkXbDGlU.HktGx$L7xkRSQYLe/yCbz6hIM2JSY6EMtkr/CyvR71Bhr9VkotfEOUiwY8A0rAuSFmO1titWLA8hTKQXWl3ZX0QqokS0'
# => 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.

Raises:

  • (NotImplementedError)

    don't know how to parse hash.



129
130
131
132
133
# File 'lib/crypt_checkpass/sha2.rb', line 129

def self.checkpass? pass, hash
  require 'unix-crypt', 'unix_crypt'

  return UnixCrypt.valid? pass, hash
end

.newhash(pass, id: 'sha256', rounds: nil) ⇒ String

Note:

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

Generate a new password hash string.



149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/crypt_checkpass/sha2.rb', line 149

def self.newhash pass, id: 'sha256', rounds: nil
  require 'unix-crypt', 'unix_crypt'

  case id
  when 'sha256' then
    klass = UnixCrypt::SHA256
  when 'sha512' then
    klass = UnixCrypt::SHA512
  else
    raise ArgumentError, 'unknown id: %p', id
  end
  return klass.build pass, nil, rounds
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', ...



136
137
138
139
140
141
142
# File 'lib/crypt_checkpass/sha2.rb', line 136

def self.provide? id
  case id when 'sha256', 'sha512' then
    return true
  else
    return false
  end
end

.understand?(str) ⇒ true, false

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



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/crypt_checkpass/sha2.rb', line 108

def self.understand? str
  md = %r{
    (?<id>     5 | 6                ){0}
    (?<rounds> rounds=[1-9]\d{3,8}  ){0}
    (?<salt>   [A-Za-z0-9./]{,16}   ){0}
    (?<csum>   [A-Za-z0-9./]{43,86} ){0}

    \A     [$] \g<id>
       (?: [$] \g<rounds> )?
           [$] \g<salt>
           [$] \g<csum>
    \z
  }x.match str
  return false unless md
  case md['id']
  when '5' then return md['csum'].length == 43
  when '6' then return md['csum'].length == 86
  end
end