module UrlHash
Constants
- ALGORITHM
- DEFAULT_HASH_LENGTH
Public Class Methods
from_hash(hash, options = {})
click to toggle source
convert an hash, as produced by to_hash
, back into the original integer
for example. UrlHash.to_hash
('+xDdeave3') -> 1001
options:
:key, :iv => provide these again if you are encrypting hashes
# File lib/url_hash.rb, line 43 def self.from_hash(hash, options = {}) buffer = Base64.decode64(hash.tr("-", "+")) if (options.has_key? :key and options.has_key? :iv) # encrypt the buffer buffer = self.encrypt(buffer, false, options) end buffer_to_int(buffer) end
to_hash(id, options = {})
click to toggle source
convert an id (an integer) to a hash, a URL-compatible string.
for example. UrlHash.to_hash(1001)
-> '+xDdeave3'
options:
:hash_length => how many characters to use for the hash. Defaults to 8. descreasing this increase the probability of a collision :key, :iv => provide these two options if you want to encrypt the hash. otherwise, it will relatively trivial for users to predict your hashes, and to work out ids based on them. Decide for yourself if this is a problem. It is recommended to generate them using Digest::SHA2.hexdigest.
# File lib/url_hash.rb, line 19 def self.to_hash(id, options = {}) options = {:hash_length => DEFAULT_HASH_LENGTH}.merge(options) # buffers use all 256 bytes, hashes just 64, thus we can only use 3/4 the numbers # -> 256 ** buffer_length <= 64 ** hash_length buffer_length = (3 * options[:hash_length] / 4).floor buffer = int_to_buffer(id, buffer_length) if (options.has_key? :key and options.has_key? :iv) # encrypt the buffer buffer = self.encrypt(buffer, true, options) end # use -'s instead of + as we like that better Base64.encode64(buffer).tr("+", "-").strip end
Private Class Methods
buffer_to_int(buffer)
click to toggle source
# File lib/url_hash.rb, line 80 def self.buffer_to_int(buffer) int = 0 multiplier = 1 buffer.each_byte do |b| int += multiplier * b multiplier *= 0x100 end int end
encrypt(buffer, forward, options)
click to toggle source
# File lib/url_hash.rb, line 55 def self.encrypt(buffer, forward, options) c = OpenSSL::Cipher::Cipher.new(ALGORITHM) forward ? c.encrypt : c.decrypt c.key = options[:key] c.iv = options[:iv] e = c.update(buffer) e << c.final e end
int_to_buffer(int, size)
click to toggle source
turn a integer into a string representation. we need to ensure that we use put the non-zero stuff on the the MSB side so that it randomizes the string properly: see : www.columbia.edu/~ariel/ssleay/fip81/fip81.html#td2
# File lib/url_hash.rb, line 71 def self.int_to_buffer(int, size) "".tap do |buf| size.times do buf << (int & 0xff) int /= 0x100 end end end