Skip to content

Instantly share code, notes, and snippets.

@athurg
Forked from trans/crypt3.rb
Last active August 29, 2015 14:09
Show Gist options
  • Save athurg/baa82f2caea8adb75dac to your computer and use it in GitHub Desktop.
Save athurg/baa82f2caea8adb75dac to your computer and use it in GitHub Desktop.
独立于操作系统平台的Crypt函数(在各平台,比如OS X、Linux都可获得一致的结果)。
# Crypt3
#
# A ruby version of crypt(3), a salted one-way hashing of a password.
#
# The Ruby version was written by Poul-Henning Kamp.
#
# Copyright (c) 2002 Poul-Henning Kamp
#
# Adapted by guillaume__dot__pierronnet_at__laposte__dot_net based on
# * http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/325204/index_txt
# which is based on FreeBSD src/lib/libcrypt/crypt.c 1.2
# * http://www.freebsd.org/cgi/cvsweb.cgi/~checkout~/src/lib/libcrypt/crypt.c?rev=1.2&content-type=text/plain
#
# [Original License]
#
# "THE BEER-WARE LICENSE" (Revision 42):
# <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
# can do whatever you want with this stuff. If we meet some day, and you think
# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
module Crypt3
VERSION = '1.1.4' #:erb: VERSION = '<%= version %>'
ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
module ImpOrd2String; def [](*args); self.slice(*args).ord; end; end
# 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.
#
# Automatically generate a 8-bytes salt if nil.
#
# Output a length hashed and salted string with size of
# magic.size + salt.size + 23.
#
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")
end
digest_class = Digest.const_get(algo.to_s.upcase)
# The password first, since that is what is most unknown. Then our magic string. Then the raw salt.
m = digest_class.new
m.update(password + magic + salt)
# Then just as many characters of the MD5(pw,salt,pw)
mixin = digest_class.new.update(password + salt + password).digest
password.length.times do |i|
m.update(mixin[i % 16].chr)
end
# Then something really weird...
# Also really broken, as far as I can tell. -m
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
# and now, just to make sure things don't run too fast
1000.times do |i|
m2 = digest_class.new
if (i & 1) != 0
m2.update(password)
else
m2.update(final)
end
if (i % 3) != 0
m2.update(salt)
end
if (i % 7) != 0
m2.update(password)
end
if (i & 1) != 0
m2.update(final)
else
m2.update(password)
end
final = m2.digest
end
# This is the bit that uses to64() in the original code.
rearranged = ""
if defined?("has_ord?".ord)
final.extend ImpOrd2String
end
[ [0, 6, 12], [1, 7, 13], [2, 8, 14], [3, 9, 15], [4, 10, 5] ].each do |a, b, c|
v = final[a] << 16 | final[b] << 8 | final[c]
4.times do
rearranged += ITOA64[v & 0x3f].chr
v >>= 6
end
end
v = final[11]
2.times do
rearranged += ITOA64[v & 0x3f].chr
v >>= 6
end
magic + salt + '$' + rearranged
end
# check the validity of a password against an hashed string
def self.check(password, hash, algo = :md5)
magic, salt = hash.split('$')[1,2]
magic = '$' + magic + '$'
self.crypt(password, algo, salt, magic) == hash
end
# generate a +size+ length random salt
def self.generate_salt(size)
(1..size).collect { ITOA64[rand(ITOA64.size)].chr }.join("")
end
# # Binary XOR of two strings.
# #
# # a = xor("\000\000\001\001", "\000\001\000\001")
# # b = xor("\003\003\003", "\000\001\002")
# #
# # a #=> "\000\001\001\000"
# # b #=> "\003\002\001"
# #
# def xor(string1, string2)
# a = string1.unpack('C'*(self.length))
# b = string2.unpack('C'*(aString.length))
# if (b.length < a.length)
# (a.length - b.length).times { b << 0 }
# end
# xor = ""
# 0.upto(a.length-1) { |pos|
# x = a[pos] ^ b[pos]
# xor << x.chr()
# }
# return(xor)
# end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment