Skip to content

Instantly share code, notes, and snippets.

@Tronix117
Created August 12, 2012 19:22
Show Gist options
  • Save Tronix117/3333894 to your computer and use it in GitHub Desktop.
Save Tronix117/3333894 to your computer and use it in GitHub Desktop.
Algorithm: Rails crypt integer to random short string, can be used as link shortener (by directly crypting databse ids). 1296 possibility for each Int (for each crypting key). String generated are not linear and also include a validity check (hashsum), an
##
# MonkeyPatch crypt_id
#
# Provide an efficient way to crypt an id, with
# different possibility for each id.
#
# Three provided methods are:
# Int.crypt_id(key='')
# String.decrypt_id(key='')
# String.change_charset(charset_from, charset_to)
#
# Each generated ID will be randomly different (1296 possibility), during
# the decryption if the input string doesn't correspond
# to an id, nil will be returned.
#
# Use (A-Z_) for the crypting key
# 123456.crypt_id('MY_KEY') # return 'fk7qCo' (1296 different output possibility for each input number with the same crypting key)
# 123456.crypt_id('MY_KEY') # return 'piThR2'
# 123456.crypt_id('MY_KEY') # return 'kEANeq'
# 'kEANeq'.decrypt_id('MY_KEY') # return 123456
#
# 123456.crypt_id('MY_KEY').decrypt_id('WRONG_KEY') # return nil
# 123456.crypt_id('MY_KEY').decrypt_id('MY_KEY') # return 123456
#
# Using a LCG pseudo random generator, not using the builtin one
# because if it ever change one day, no previously generated
# crypted string will be able to be decrypted anymore.
#
# It's strongly advised to changed @a and @c variable of
# the LCGRandom generator in a production environment, it
# increase the security. Look at this article to choose
# those two values correctly:
# http://en.wikipedia.org/wiki/Linear_congruential_generator
#
# @author Jeremy Trufier <[email protected]>
# @url https://gist.github.com/gists/3333894
##
# create a pseudorandom function easy to reproduce
# in case we change langage or the suite change
# one day in ruby
# http://en.wikipedia.org/wiki/Linear_congruential_generator
class LCGRandom
def initialize(n)
@seed = n || Time.now.usec
@a = 1103515245 # same as GCC
@c = 12345 # same as GCC
@m = 2**31 # same as GCC
end
def srand(n)
@seed = n
self
end
def rand(l = nil)
@seed = (@a * @seed + @c) % @m
return @seed.to_f / @m if l == nil
@seed % l
end
end
class Integer
def crypt_id(key = '')
lcgr = LCGRandom.new(Time.now.usec)
id = self
a = (('1'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a + ['$','_']).join
k = (('0'..'9').to_a + ('a'..'z').to_a).sample(2).join
id = (id + 1).to_s(32)
lcgr.srand k.to_i(36) * key.to_i(36)
p = lcgr.rand
a.size.times do |i|
r = i + lcgr.rand(a.size - i)
a[i], a[r] = a[r], a[i]
end
a = '0' + a
h = 0
id.length.times{|i| h += id[i].to_i(32)}
h = h % 32
id += h.to_s(32)
l = id.length
p = (p * l).floor
id[p], id[l - 1] = id[l - 1], id[p]
k[1] + id.change_charset(('0'..'9').to_a + ('a'..'v').to_a, a) + k[0]
end
end
class String
def change_charset(f, t)
def bin_fix_length(b, s)
b = '0' + b while b.length < s
b
end
str = self.clone
l = (f.length - 1).to_s(2).length
str_c = ''
str_c += bin_fix_length(f.index(str.slice!(0)).to_s(2), l) until str == ''
str_c.slice!(0) while str_c.slice(0) == '0'
l = t.length.to_s(2).length - 1
s = (str_c.length.to_f / l).ceil * l
str_c = bin_fix_length(str_c, s)
str = t[str_c.slice!(-l,l).to_i(2)] + str until str_c == ''
str
end
def decrypt_id(key = '')
id = self.clone
return nil if id.length <= 2
a = (('1'..'9').to_a + ('a'..'z').to_a + ('A'..'Z').to_a + ['$','_']).join
k = id.slice!(- 1, 1) + id.slice!(0)
lcgr = LCGRandom.new(k.to_i(36) * key.to_i(36))
p = lcgr.rand
a.size.times do |i|
r = i + lcgr.rand(a.size - i)
a[i], a[r] = a[r], a[i]
end
a = '0' + a
id = id.change_charset(a, ('0'..'9').to_a + ('a'..'v').to_a)
l = id.length
p = (p * l).floor
id[p], id[l - 1] = id[l - 1], id[p]
h = id.slice!(-1,1).to_i(32)
h2 = 0
id.length.times{|i| h2 += id[i].to_i(32)}
return nil if (h2 % 32) != h
id.to_i(32) - 1
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment