Class: Passlib::LdapDigest
- Defined in:
- lib/passlib/ldap_digest.rb
Overview
Handles LDAP RFC 2307 digest password hashes.
Supports plain and salted variants for MD5, SHA-1, SHA-256, SHA-512, SHA3-256, and SHA3-512. MD5 and SHA-1 hashes may be stored in hex encoding (as produced by some LDAP implementations) or standard base64, both are detected automatically on load and preserved on round-trips.
Scheme names and their corresponding LDAP prefix:
-
MD5/SMD5(salted) -
SHA/SSHA(salted) -
SHA256/SSHA256(salted) -
SHA512/SSHA512(salted) -
SHA3-256/SSHA3-256(salted) -
SHA3-512/SSHA3-512(salted)
Hash format: {SSHA512}base64data (or {MD5}hexdata for hex-encoded MD5/SHA)
Constant Summary
Constants included from Internal::DSL
Instance Attribute Summary
Attributes inherited from Password
Class Method Summary collapse
-
.create(secret, **options) ⇒ LdapDigest
Creates a new LDAP digest hash.
Instance Method Summary collapse
-
#create_comparable(secret) ⇒ LdapDigest
A new instance hashed with the same scheme, salt, and encoding.
- #upgrade? ⇒ Boolean
Methods inherited from Password
available?, #initialize, #inspect, load, #pretty_print, #verify
Methods included from Internal::DSL
Constructor Details
This class inherits a constructor from Passlib::Password
Class Method Details
.create(secret, **options) ⇒ LdapDigest
Creates a new LDAP digest hash.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/passlib/ldap_digest.rb', line 39 class LdapDigest < Password SCHEMES = { "MD5" => { digest: "MD5", size: 16, salted: false }, "SHA" => { digest: "SHA1", size: 20, salted: false }, "SHA256" => { digest: "SHA256", size: 32, salted: false }, "SHA512" => { digest: "SHA512", size: 64, salted: false }, "SHA3-256" => { digest: "SHA3-256", size: 32, salted: false }, "SHA3-512" => { digest: "SHA3-512", size: 64, salted: false }, "SMD5" => { digest: "MD5", size: 16, salted: true, salt_size: 4 }, "SSHA" => { digest: "SHA1", size: 20, salted: true, salt_size: 4 }, "SSHA256" => { digest: "SHA256", size: 32, salted: true, salt_size: 8 }, "SSHA512" => { digest: "SHA512", size: 64, salted: true, salt_size: 8 }, "SSHA3-256" => { digest: "SHA3-256", size: 32, salted: true, salt_size: 8 }, "SSHA3-512" => { digest: "SHA3-512", size: 64, salted: true, salt_size: 8 }, }.freeze LOAD_PATTERN = /\A\{([A-Z0-9-]+)\}([A-Za-z0-9+\/=]+)\z/i DEFAULT = "SSHA512" HEX_SCHEMES = %w[MD5 SHA].freeze private_constant :SCHEMES, :LOAD_PATTERN, :DEFAULT, :HEX_SCHEMES # Register all scheme names (including hyphenated SHA3 variants) so the # updated LDAP auto-detection regex can match them directly. SCHEMES.each_key { Internal::Register::LDAP_IDS[_1] = self } register :ldap_digest :variant, :salt, :hex # @param secret [String] the plaintext password to re-hash # @return [LdapDigest] a new instance hashed with the same scheme, salt, and encoding def create_comparable(secret) = self.class.create(secret, variant: @variant, salt: @salt, hex: @hex) def upgrade? configured = normalize_variant(config.variant) @variant != configured end private def normalize_variant(input) return DEFAULT unless input input.to_s.upcase.tr("_", "-") end def create(secret) @variant = normalize_variant(config.variant) scheme = SCHEMES[@variant] or raise ArgumentError, "unknown LDAP scheme: #{@variant.inspect}" @salt = config.salt || (scheme[:salted] ? Internal.random_bytes(scheme[:salt_size]) : nil) @hex = config.hex checksum = OpenSSL::Digest.digest(scheme[:digest], secret.to_s.b + @salt.to_s.b) data = scheme[:salted] ? checksum + @salt : checksum @hex ? "{#{@variant}}#{data.unpack1("H*")}" : "{#{@variant}}#{[data].pack("m0")}" end def load(string) m = LOAD_PATTERN.match(string) or raise UnknownHashFormat, "invalid LDAP hash: #{string.inspect}" @variant = normalize_variant(m[1]) scheme = SCHEMES[@variant] or raise UnknownHashFormat, "unknown LDAP scheme: #{@variant.inspect}" @hex = HEX_SCHEMES.include?(@variant) && m[2].length == scheme[:size] * 2 && m[2].match?(/\A[0-9a-f]+\z/i) raw = @hex ? [m[2]].pack("H*") : m[2].unpack1("m") @salt = raw[scheme[:size]..] if scheme[:salted] string end end |
Instance Method Details
#create_comparable(secret) ⇒ LdapDigest
Returns a new instance hashed with the same scheme, salt, and encoding.
70 |
# File 'lib/passlib/ldap_digest.rb', line 70 def create_comparable(secret) = self.class.create(secret, variant: @variant, salt: @salt, hex: @hex) |
#upgrade? ⇒ Boolean
72 73 74 75 |
# File 'lib/passlib/ldap_digest.rb', line 72 def upgrade? configured = normalize_variant(config.variant) @variant != configured end |