class Nova::Starbound::Encryptors::OpenSSL

Handles encryption using the OpenSSL library. Shares the shared secret using RSA public key encryption, creates a HMAC digest of the body using the shared secret as a key, and encrypts the body using AES-256-CBC encryption.

Constants

RSA_KEY_SIZE

The RSA key size for the key exchange.

SECRET_SIZE

The shared secret size, in bytes. If RSA_KEY_SIZE is 4096, this is 256.

Public Class Methods

available?() click to toggle source

see Encryptor.available?

# File lib/nova/starbound/encryptors/openssl.rb, line 24
def self.available?
  @_available ||= begin
    require 'openssl'
    true
  rescue LoadError
    false
  end
end

Public Instance Methods

decrypt(packet) click to toggle source

(see Encryptor#decrypt)

# File lib/nova/starbound/encryptors/openssl.rb, line 50
def decrypt(packet)
  packet = packet.clone
  decipher = ::OpenSSL::Cipher::AES256.new(:CBC)
  decipher.decrypt
  decipher.key = options[:shared_secret]
  decipher.iv  = packet[:nonce]

  digest = packet[:body][0..63]
  actual_body = packet[:body][64..-1]

  if hmac_digest(actual_body) != digest
    raise EncryptorError, "Digest doesn't match the body."
  end

  packet.body = decipher.update(actual_body) +
    decipher.final
  packet
end
encrypt(packet) click to toggle source

(see Encryptor#encrypt)

# File lib/nova/starbound/encryptors/openssl.rb, line 34
def encrypt(packet)
  packet = packet.clone
  cipher = ::OpenSSL::Cipher::AES256.new(:CBC)
  cipher.encrypt
  cipher.key = options[:shared_secret]

  # we have to fit the packet's nonce size.
  packet[:nonce] = cipher.iv = ::OpenSSL::Random.random_bytes(24)

  encrypted = cipher.update(packet[:body]) + cipher.final

  packet.body = hmac_digest(encrypted) + encrypted
  packet
end
other_public_key=(public_key) click to toggle source

If we already have a public key, that means that the value that’s passed to this method is the shared secret. Otherwise, it really is the public key of the other remote. If the passed value is a shared secret, it’s decrypted with our private key and stored. If it’s the other public key, it’s instantized to a openssl RSA key, and stored.

@return [void]

# File lib/nova/starbound/encryptors/openssl.rb, line 99
def other_public_key=(public_key)
  if options[:public]
    options[:shared_secret] =
      options[:private].private_decrypt(public_key)
  else
    options[:other_public] =
      ::OpenSSL::PKey::RSA.new(public_key)
  end
end
private_key!() click to toggle source

(see Encryptor#private_key!)

# File lib/nova/starbound/encryptors/openssl.rb, line 70
def private_key!
  options[:private] = ::OpenSSL::PKey::RSA.new(RSA_KEY_SIZE)
end
public_key() click to toggle source

If we have already recieved the other public key, we’ll generate the secret here, and return the encrypted version of that secret here. Otherwise, we’ll generate our private key and return that in DER format.

@return [String]

# File lib/nova/starbound/encryptors/openssl.rb, line 80
def public_key
  if options[:other_public]
    options[:shared_secret] =
      ::OpenSSL::Random.random_bytes(SECRET_SIZE)
    options[:other_public].public_encrypt(
      options[:shared_secret])
  else
    options[:public] ||= options[:private].public_key.to_der
  end
end

Private Instance Methods

hmac_digest(body) click to toggle source

Provides a digest of the data, using HMAC.

@return [String]

# File lib/nova/starbound/encryptors/openssl.rb, line 114
def hmac_digest(body)
  ::OpenSSL::HMAC.digest(::OpenSSL::Digest::SHA512.new,
    options[:shared_secret], body.to_s)
end