require 'digest' # Constants ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" def to64(value, length) result = "" length.times do result << ITOA64[value & 0x3f] value >>= 6 end result end def gossamer_md5_crypt(password, legacy_hash) # Extract the salt from the legacy hash parts = legacy_hash.split('$') salt = parts[2] # Limit the salt to 8 characters salt = salt[0, 8] magic = "$GT$" ctx = Digest::MD5.new ctx.update(password) ctx.update(magic) ctx.update(salt) final = Digest::MD5.new final.update(password) final.update(salt) final.update(password) final_digest = final.digest password_length = password.length while password_length > 0 ctx.update(final_digest[0, [password_length, 16].min]) password_length -= 16 end password_length = password.length while password_length > 0 if password_length & 1 != 0 ctx.update("\x00") else ctx.update(password[0, 1]) end password_length >>= 1 end final_digest = ctx.digest 1000.times do |i| ctx1 = Digest::MD5.new if i & 1 != 0 ctx1.update(password) else ctx1.update(final_digest) end ctx1.update(salt) if i % 3 != 0 ctx1.update(password) if i % 7 != 0 if i & 1 != 0 ctx1.update(final_digest) else ctx1.update(password) end final_digest = ctx1.digest end result = '' result << to64((final_digest[0].ord << 16) | (final_digest[6].ord << 8) | final_digest[12].ord, 4) result << to64((final_digest[1].ord << 16) | (final_digest[7].ord << 8) | final_digest[13].ord, 4) result << to64((final_digest[2].ord << 16) | (final_digest[8].ord << 8) | final_digest[14].ord, 4) result << to64((final_digest[3].ord << 16) | (final_digest[9].ord << 8) | final_digest[15].ord, 4) result << to64((final_digest[4].ord << 16) | (final_digest[10].ord << 8) | final_digest[5].ord, 4) result << to64(final_digest[11].ord, 2) "#{magic}#{salt}$#{result}" end def verify_gossamer_password(password, legacy_hash) generated_hash = gossamer_md5_crypt(password, legacy_hash) generated_hash == legacy_hash end # Example usage legacy_hash = "$GT$i5ZNZdfX$077FK6JU70HIr2pR1/uLP1" password = "polyflex80" resulting_hash = gossamer_md5_crypt(password, legacy_hash) puts "Generated hash: #{resulting_hash}" puts "Legacy hash: #{legacy_hash}" puts "Password verification: #{verify_gossamer_password(password, legacy_hash)}"