Skip to content

Instantly share code, notes, and snippets.

@elsurudo
Created March 25, 2019 13:59
Show Gist options
  • Save elsurudo/b93ac65c74500386a2c07423e62b30f5 to your computer and use it in GitHub Desktop.
Save elsurudo/b93ac65c74500386a2c07423e62b30f5 to your computer and use it in GitHub Desktop.
Knuthy Obfuscation (Optimus Prime)
# app/models/concerns/obfuscation.rb
# ID obfuscation using Optimus algorithm
# TODO if we want to use this with more classes, it'd
# be best to make sure we use a different random seed
# for each model.
# Do something like this: http://jes.al/2014/11/creating-rails-concern-accepts-argumentsparameters/
module Obfuscation
extend ActiveSupport::Concern
module ClassMethods
def obfuscate_id(n)
OPTIMUS.encode(n.to_i)
end
def deobfuscate_id(str)
OPTIMUS.decode(str.to_i)
end
def find_by_obfuscated_id(id)
find_by(id: OPTIMUS.decode(id.to_i))
end
def find_by_obfuscated_id!(id)
find_by!(id: OPTIMUS.decode(id.to_i))
end
end
def obfuscated_id
OPTIMUS.encode(id.to_i)
end
def to_param
respond_to?(:title) && title.present? ? "#{obfuscated_id}-#{title.parameterize}" : obfuscated_id.to_s
end
end
# lib/util/optimus.rb
# Donald Knuth's two-way fast hashing algorithm
# More info: https://jenssegers.com/67/id-transformation-with-optimus
# Based on: https://github.com/jenssegers/optimus
# Generate prime, inverse, and random xor with `php vendor/bin/optimus spark` (must have above installed via PHP composer)
# Numeric alternative to: http://hashids.org/ruby/
module Optimus
class Optimus
MAX_INT = 2147483647 # based on Postgres' integer/int4 maximum
def initialize(prime, inverse, xor = 0)
raise ArgumentError, "prime must be an integer" unless (prime.is_a?(Numeric) && prime.integer?)
raise ArgumentError, "inverse must be an integer" unless (inverse.is_a?(Numeric) && inverse.integer?)
raise ArgumentError, "xor must be an integer" unless (xor.is_a?(Numeric) && xor.integer?)
@prime = prime.to_i
@inverse = inverse.to_i
@xor = xor.to_i
end
def encode(value)
raise ArgumentError, "Value to encode must be an integer" unless (value.is_a?(Numeric) && value.integer?)
((value.to_i * @prime) & MAX_INT) ^ @xor
end
def decode(value)
raise ArgumentError, "Value to decode must be an integer" unless (value.is_a?(Numeric) && value.integer?)
((value.to_i ^ @xor) * @inverse) & MAX_INT
end
end
end
# config/initializers/optimus_primes.rb
OPTIMUS = Optimus::Optimus.new(ENV['optimus_prime'].to_i, ENV['optimus_inverse'].to_i, ENV['optimus_random'].to_i)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment