Created
August 12, 2012 19:22
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# 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