Module: Crypt3
- Defined in:
- lib/standard/facets/crypt3.rb
Overview
Crypt3 is a pure Ruby version of crypt(3), a salted one-way hashing of a password.
Supported hashing algorithms are: md5, sha1, sha256, sha384, sha512, rmd160.
Only the md5 hashing algorithm is standard and compatible with crypt(3), the others are not standard.
Originally written by Poul-Henning Kamp (Beer-Ware License). Adapted by guillaume.pierronnet based on FreeBSD src/lib/libcrypt/crypt.c
Copyright © 2002 Poul-Henning Kamp License: BSD-2-Clause
Constant Summary collapse
- VERSION =
'1.2.0'- ITOA64 =
Base 64 character set.
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
Class Method Summary collapse
-
.check(password, hash, algo = :md5) ⇒ Object
Check the validity of a password against a hashed string.
-
.crypt(password, algo = :md5, salt = nil, magic = '$1$') ⇒ Object
A pure ruby version of crypt(3), a salted one-way hashing of a password.
-
.generate_salt(size) ⇒ Object
Generate a random salt of the given size.
Class Method Details
.check(password, hash, algo = :md5) ⇒ Object
Check the validity of a password against a hashed string.
Crypt3.check('password', '$1$xxxxxxxx$hash...') #=> true
password - The phrase that was encrypted. [String] hash - The cryptographic hash. [String] algo - The algorithm used. [Symbol]
Returns true if valid. [Boolean]
109 110 111 112 113 |
# File 'lib/standard/facets/crypt3.rb', line 109 def self.check(password, hash, algo = :md5) magic, salt = hash.split('$')[1, 2] magic = '$' + magic + '$' self.crypt(password, algo, salt, magic) == hash end |
.crypt(password, algo = :md5, salt = nil, magic = '$1$') ⇒ Object
A pure ruby version of crypt(3), a salted one-way hashing of a password.
Automatically generates an 8-byte salt if none given.
Crypt3.crypt('password') #=> "$1$xxxxxxxx$hash..."
Crypt3.crypt('password', :sha256) #=> "$1$xxxxxxxx$hash..."
password - The phrase to encrypt. [String] algo - The algorithm to use. [Symbol] salt - Cryptographic salt, random if nil. [String] magic - The magic prefix. [String]
Returns the cryptographic hash. [String]
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 |
# File 'lib/standard/facets/crypt3.rb', line 35 def self.crypt(password, algo = :md5, salt = nil, magic = '$1$') salt ||= generate_salt(8) case algo when :md5 require "digest/md5" when :sha1 require "digest/sha1" when :rmd160 require "digest/rmd160" when :sha256, :sha384, :sha512 require "digest/sha2" else raise(ArgumentError, "unknown algorithm: #{algo}") end digest_class = Digest.const_get(algo.to_s.upcase) m = digest_class.new m.update(password + magic + salt) mixin = digest_class.new.update(password + salt + password).digest password.length.times do |i| m.update(mixin[i % 16].chr) end i = password.length while i != 0 if (i & 1) != 0 m.update("\x00") else m.update(password[0].chr) end i >>= 1 end final = m.digest 1000.times do |i| m2 = digest_class.new m2.update((i & 1) != 0 ? password : final) m2.update(salt) if (i % 3) != 0 m2.update(password) if (i % 7) != 0 m2.update((i & 1) != 0 ? final : password) final = m2.digest end rearranged = "" [[0, 6, 12], [1, 7, 13], [2, 8, 14], [3, 9, 15], [4, 10, 5]].each do |a, b, c| v = final.getbyte(a) << 16 | final.getbyte(b) << 8 | final.getbyte(c) 4.times do rearranged += ITOA64[v & 0x3f] v >>= 6 end end v = final.getbyte(11) 2.times do rearranged += ITOA64[v & 0x3f] v >>= 6 end magic + salt + '$' + rearranged end |
.generate_salt(size) ⇒ Object
Generate a random salt of the given size.
Crypt3.generate_salt(8) #=> "xK3d9Wq2"
size - The size of the salt. [Integer]
Returns random salt. [String]
122 123 124 |
# File 'lib/standard/facets/crypt3.rb', line 122 def self.generate_salt(size) (1..size).map { ITOA64[rand(ITOA64.size)] }.join end |