1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 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 106 107 108 109
|
require 'English'
require 'base64'
require 'digest/md5'
require 'digest/sha1'
module ActiveLdap
module UserPassword
include GetText
module_function
def valid?(password, hashed_password)
unless /^\{([A-Za-z][A-Za-z\d]+)\}/ =~ hashed_password
# Plain text password
return hashed_password == password
end
type = $1
hashed_password_without_type = $POSTMATCH
normalized_type = type.downcase
unless respond_to?(normalized_type)
raise ArgumentError, _("Unknown Hash type: %s") % type
end
salt_extractor = "extract_salt_for_#{normalized_type}"
if respond_to?(salt_extractor)
salt = send(salt_extractor, hashed_password_without_type)
if salt.nil?
raise ArgumentError,
_("Can't extract salt from hashed password: %s") % hashed_password
end
generated_password = send(normalized_type, password, salt)
else
generated_password = send(normalized_type, password)
end
hashed_password == generated_password
end
def crypt(password, salt=nil)
salt ||= "$1$#{Salt.generate(8)}"
"{CRYPT}#{password.crypt(salt)}"
end
def extract_salt_for_crypt(crypted_password)
if /\A\$(?:1|5|6|2a)\$[a-zA-Z0-9.\/]{,16}\$/ =~ crypted_password
$MATCH
else
salt = crypted_password[0, 2]
if salt.size != 2
raise ArgumentError, _("salt size must be 2: <%s>") % salt
end
unless /\A[a-zA-Z0-9.\/]{2}\z/ =~ salt
message = _("salt character must be [a-zA-Z0-9./]: <%s>") % salt
raise ArgumentError, message
end
salt
end
end
def md5(password)
"{MD5}#{[Digest::MD5.digest(password)].pack('m').chomp}"
end
def smd5(password, salt=nil)
if salt and salt.size < 4
raise ArgumentError, _("salt size must be >= 4: %s") % salt.inspect
end
salt ||= Salt.generate(4)
md5_hash_with_salt = "#{Digest::MD5.digest(password + salt)}#{salt}"
"{SMD5}#{[md5_hash_with_salt].pack('m').gsub("\n", '')}"
end
def extract_salt_for_smd5(smd5ed_password)
extract_salt_at_pos(smd5ed_password, 16)
end
def sha(password)
"{SHA}#{[Digest::SHA1.digest(password)].pack('m').chomp}"
end
def ssha(password, salt=nil)
if salt and salt.size < 4
raise ArgumentError, _("salt size must be >= 4: %s") % salt.inspect
end
salt ||= Salt.generate(4)
sha1_hash_with_salt = "#{Digest::SHA1.digest(password + salt)}#{salt}"
"{SSHA}#{[sha1_hash_with_salt].pack('m').gsub("\n", '')}"
end
def extract_salt_for_ssha(sshaed_password)
extract_salt_at_pos(sshaed_password, 20)
end
def extract_salt_at_pos(hashed_password, position)
salt = Base64.decode64(hashed_password)[position..-1]
salt == '' ? nil : salt
end
module Salt
CHARS = ['.', '/'] + ['0'..'9', 'A'..'Z', 'a'..'z'].collect do |x|
x.to_a
end.flatten
module_function
def generate(length)
salt = ""
length.times {salt << CHARS[rand(CHARS.length)]}
salt
end
end
end
end
|