module Vidibus::Secure

Constants

VERSION

Public Class Methods

decrypt(data, key, options = {}) click to toggle source

Decrypts given data with given key.

# File lib/vidibus/secure.rb, line 57
def decrypt(data, key, options = {})
  raise KeyError.new("Please provide a secret key to decrypt data with.") unless key
  options = settings[:crypt].merge(options)
  decoded_data = decode(data, options)
  decrypted_data = crypt(:decrypt, decoded_data, key, options)
  return data unless decrypted_data
  begin
    JSON.parse(decrypted_data)
  rescue JSON::ParserError
    decrypted_data
  end
end
encrypt(data, key, options = {}) click to toggle source

Encrypts given data with given key.

# File lib/vidibus/secure.rb, line 46
def encrypt(data, key, options = {})
  raise KeyError.new("Please provide a secret key to encrypt data with.") unless key
  options = settings[:crypt].merge(options)
  unless data.is_a?(String)
    data = JSON.generate(data)
  end
  encrypted_data = crypt(:encrypt, data, key, options)
  encode(encrypted_data, options)
end
random(options = {}) click to toggle source

Returns a truly random string. Now it is not much more than an interface for Ruby's SecureRandom, but that might change over time.

Options:

:length     Length of string to generate
:encoding   Encoding of string; hex or base64

Keep in mind that a hexadecimal string is less secure than a base64 encoded string with the same length!

# File lib/vidibus/secure.rb, line 30
def random(options = {})
  options = settings[:random].merge(options)
  length = options[:length]
  SecureRandom.send(options[:encoding], length)[0,length]
end
settings() click to toggle source

Define default settings for random, sign, and crypt.

# File lib/vidibus/secure.rb, line 11
def settings
  @settings ||= {
    :random => { :length => 50, :encoding => :base64 },
    :sign => { :algorithm => "SHA256", :encoding => :hex },
    :crypt => { :algorithm => "AES-256-CBC", :encoding => :base64 }
  }
end
sign(data, key, options = {}) click to toggle source

Returns signature of given data with given key.

# File lib/vidibus/secure.rb, line 37
def sign(data, key, options = {})
  raise KeyError.new("Please provide a secret key to sign data with.") unless key
  options = settings[:sign].merge(options)
  digest = OpenSSL::Digest.new(options[:algorithm])
  signature = OpenSSL::HMAC.digest(digest, key, data)
  encode(signature, options)
end
sign_request(verb, path, params, key, signature_param = nil) click to toggle source

Signs request.

# File lib/vidibus/secure.rb, line 71
def sign_request(verb, path, params, key, signature_param = nil)
  default_signature_param = :sign
  params_given = !!params
  raise InputError.new("Given params is not a Hash.") if params_given and !params.is_a?(Hash)
  params = {} unless params_given
  signature_param ||= (params_given and params.keys.first.is_a?(String)) ? default_signature_param.to_s : default_signature_param

  uri = URI.parse(path)
  path_params = Rack::Utils.parse_nested_query(uri.query)
  uri.query = nil

  _verb = verb.to_s.downcase
  _params = (params.merge(path_params)).except(signature_param.to_s, signature_param.to_s.to_sym)

  signature_string = [
    _verb,
    uri.to_s.gsub(/\/+$/, ""),
    _params.any? ? params_identifier(_params) : ""
  ].join("|")

  signature = sign(signature_string, key)

  if %w[post put].include?(_verb) or (params_given and path_params.empty?)
    params[signature_param] = signature
  else
    unless path.gsub!(/(#{signature_param}=)[^&]+/, "\\1#{signature}")
      glue = path.match(/\?/) ? "&" : "?"
      path << "#{glue}#{signature_param}=#{signature}"
    end
  end
  [path, params]
end
verify_request(verb, path, params, key, signature_param = nil) click to toggle source

Verifies that given request is valid.

# File lib/vidibus/secure.rb, line 105
def verify_request(verb, path, params, key, signature_param = nil)
  params ||= {}
  _path = path.dup
  _params = params.dup
  sign_request(verb, _path, _params, key, signature_param)
  return (path == _path and params == _params)
end

Protected Class Methods

crypt(cipher_method, data, key, options = {}) click to toggle source
# File lib/vidibus/secure.rb, line 115
def crypt(cipher_method, data, key, options = {})
  return unless data && data != ''
  cipher = OpenSSL::Cipher::Cipher.new(options[:algorithm])
  digest = OpenSSL::Digest::SHA512.new(key).digest
  cipher.send(cipher_method)
  cipher.pkcs5_keyivgen(digest)
  result = cipher.update(data)
  result << cipher.final
end
decode(data, options = {}) click to toggle source
# File lib/vidibus/secure.rb, line 134
def decode(data, options = {})
  return unless data
  if options[:encoding] == :hex
    [data].pack("H*")
  elsif options[:encoding] == :base64
    data.unpack("m*").join
  end
end
encode(data, options = {}) click to toggle source
# File lib/vidibus/secure.rb, line 125
def encode(data, options = {})
  return unless data
  if options[:encoding] == :hex
    data.unpack("H*").join
  elsif options[:encoding] == :base64
    [data].pack("m*")
  end
end
params_identifier(params, level = 1) click to toggle source

Returns an identifier string from given params input.

Example:

{:some=>{:nested=>{:really=>["serious", "stuff"]}, :are=>"params"}}
# => 1:some:2:are:params|2:nested:3:really:4:serious:|4:stuff:
# File lib/vidibus/secure.rb, line 149
def params_identifier(params, level = 1)
  array = []
  for key, value in params
    if value.is_a?(Array) or value.is_a?(Hash)
      value = params_identifier(value, level + 1)
    end
    array << "#{level}:#{key}:#{value}"
  end
  array.sort.join("|")
end