module Bixby::CryptoUtil

Public Class Methods

decrypt(data, key_pem, iv_pem) click to toggle source

Decrypt the given payload from over-the-wire transmission

@param [Object] data encrypted payload, usually a JSON-encoded String @param [OpenSSL::PKey::RSA] key_pem Private key of the receiver @param [OpenSSL::PKey::RSA] iv_pem Public key of the sender

# File lib/bixby-common/util/crypto_util.rb, line 41
def decrypt(data, key_pem, iv_pem)
  data = StringIO.new(data, 'rb') if not data.kind_of? StringIO
  hmac = data.readline.strip
  key = key_pem.private_decrypt(read_next(data))
  iv  = iv_pem.public_decrypt(read_next(data))

  c = new_cipher()
  c.decrypt
  c.key = key
  c.iv = iv

  payload = d64(data.read)

  # very hmac of encrypted payload
  if not verify_hmac(hmac, key, iv, payload) then
    raise Bixby::EncryptionError, "hmac verification failed", caller
  end

  data = StringIO.new(c.update(payload) + c.final)

  ts = data.readline.strip
  if (Time.new.to_i - ts.to_i) > 900 then # must be within last 15 min
    raise Bixby::EncryptionError, "payload verification failed", caller
  end

  return data.read
end
encrypt(data, uuid, key_pem, iv_pem) click to toggle source

Encrypt the given payload for over-the-wire transmission

@param [Object] data payload, usually a JSON-encoded String @param [String] uuid UUID of the sender @param [OpenSSL::PKey::RSA] key_pem Public key of the receiver @param [OpenSSL::PKey::RSA] iv_pem Private key of the sender

# File lib/bixby-common/util/crypto_util.rb, line 17
def encrypt(data, uuid, key_pem, iv_pem)
  c = new_cipher()
  c.encrypt
  key = c.random_key
  iv = c.random_iv

  data = Time.new.to_i.to_s + "\n" + data # prepend timestamp
  encrypted = c.update(data) + c.final

  out = []
  out << uuid
  out << create_hmac(key, iv, encrypted)
  out << w( key_pem.public_encrypt(key) )
  out << w( iv_pem.private_encrypt(iv) )
  out << e64(encrypted)

  return out.join("\n")
end
generate_access_key() click to toggle source

Generate a new access key

@return [String] :nocov:

# File lib/bixby-common/util/crypto_util.rb, line 80
def generate_access_key
  Digest.hexencode(Digest::MD5.new.digest(OpenSSL::Random.random_bytes(512)))
end
generate_keypair() click to toggle source

Generate a new 2048-bit RSA keypair

@return [OpenSSL::PKey::RSA]

# File lib/bixby-common/util/crypto_util.rb, line 72
def generate_keypair
  OpenSSL::PKey::RSA.generate(2048)
end
generate_secret_key() click to toggle source

Generate a new secret key

@return [String] :nocov:

# File lib/bixby-common/util/crypto_util.rb, line 89
def generate_secret_key
  Digest.hexencode(Digest::SHA2.new(512).digest(OpenSSL::Random.random_bytes(512)))
end

Private Class Methods

create_hmac(key, iv, payload) click to toggle source

Compute an HMAC using SHA2-256

@param [String] key @param [String] iv @param [String] payload encrypted payload

@return [String] digest in hexadecimal format

# File lib/bixby-common/util/crypto_util.rb, line 104
def create_hmac(key, iv, payload)
  d = Digest::SHA2.new(256)
  d << key << iv << payload
  return d.hexdigest()
end
d64(s) click to toggle source
# File lib/bixby-common/util/crypto_util.rb, line 140
def d64(s)
  Base64.decode64(s)
end
e64(s) click to toggle source
# File lib/bixby-common/util/crypto_util.rb, line 136
def e64(s)
  Base64.encode64(s)
end
new_cipher() click to toggle source
# File lib/bixby-common/util/crypto_util.rb, line 122
def new_cipher
  # TODO make this configurable? perhaps use CTR when available
  # we can store a CTR support flag on the master
  OpenSSL::Cipher.new("AES-256-CBC")
end
read_next(data) click to toggle source
# File lib/bixby-common/util/crypto_util.rb, line 132
def read_next(data)
  d64(data.readline.gsub(/\\n/, "\n"))
end
verify_hmac(hmac, key, iv, payload) click to toggle source

Verify the given HMAC of the incoming message

@param [String] hmac @param [String] key @param [String] iv @param [String] payload encrypted payload

@return [Boolean] true if hmac matches

# File lib/bixby-common/util/crypto_util.rb, line 118
def verify_hmac(hmac, key, iv, payload)
  create_hmac(key, iv, payload) == hmac
end
w(s) click to toggle source
# File lib/bixby-common/util/crypto_util.rb, line 128
def w(s)
  e64(s).gsub(/\n/, "\\n")
end