class Lockbox::Encryptor

Public Class Methods

new(**options) click to toggle source
# File lib/lockbox/encryptor.rb, line 3
def initialize(**options)
  options = Lockbox.default_options.merge(options)
  @encode = options.delete(:encode)
  # option may be renamed to binary: true
  # warn "[lockbox] Lockbox 1.0 will default to encode: true. Pass encode: false to keep the current behavior." if @encode.nil?
  previous_versions = options.delete(:previous_versions)

  @boxes =
    [Box.new(**options)] +
    Array(previous_versions).map { |v| Box.new(key: options[:key], **v) }
end

Public Instance Methods

decrypt(ciphertext, **options) click to toggle source
# File lib/lockbox/encryptor.rb, line 22
def decrypt(ciphertext, **options)
  ciphertext = Base64.decode64(ciphertext) if @encode
  ciphertext = check_string(ciphertext)

  # ensure binary
  if ciphertext.encoding != Encoding::BINARY
    # dup to prevent mutation
    ciphertext = ciphertext.dup.force_encoding(Encoding::BINARY)
  end

  @boxes.each_with_index do |box, i|
    begin
      return box.decrypt(ciphertext, **options)
    rescue => e
      # returning DecryptionError instead of PaddingError
      # is for end-user convenience, not for security
      error_classes = [DecryptionError, PaddingError]
      error_classes << RbNaCl::LengthError if defined?(RbNaCl::LengthError)
      error_classes << RbNaCl::CryptoError if defined?(RbNaCl::CryptoError)
      if error_classes.any? { |ec| e.is_a?(ec) }
        raise DecryptionError, "Decryption failed" if i == @boxes.size - 1
      else
        raise e
      end
    end
  end
end
decrypt_io(io, **options) click to toggle source
# File lib/lockbox/encryptor.rb, line 56
def decrypt_io(io, **options)
  new_io = Lockbox::IO.new(decrypt(io.read, **options))
  copy_metadata(io, new_io)
  new_io
end
decrypt_str(ciphertext, **options) click to toggle source
# File lib/lockbox/encryptor.rb, line 62
def decrypt_str(ciphertext, **options)
  message = decrypt(ciphertext, **options)
  message.force_encoding(Encoding::UTF_8)
end
encrypt(message, **options) click to toggle source
# File lib/lockbox/encryptor.rb, line 15
def encrypt(message, **options)
  message = check_string(message)
  ciphertext = @boxes.first.encrypt(message, **options)
  ciphertext = Base64.strict_encode64(ciphertext) if @encode
  ciphertext
end
encrypt_io(io, **options) click to toggle source
# File lib/lockbox/encryptor.rb, line 50
def encrypt_io(io, **options)
  new_io = Lockbox::IO.new(encrypt(io.read, **options))
  copy_metadata(io, new_io)
  new_io
end

Private Instance Methods

check_string(str) click to toggle source
# File lib/lockbox/encryptor.rb, line 69
def check_string(str)
  str = str.read if str.respond_to?(:read)
  # Ruby uses "no implicit conversion of Object into String"
  raise TypeError, "can't convert #{str.class.name} to String" unless str.respond_to?(:to_str)
  str.to_str
end
copy_metadata(source, target) click to toggle source
# File lib/lockbox/encryptor.rb, line 76
def copy_metadata(source, target)
  target.original_filename =
    if source.respond_to?(:original_filename)
      source.original_filename
    elsif source.respond_to?(:path)
      File.basename(source.path)
    end
  target.content_type = source.content_type if source.respond_to?(:content_type)
  target.set_encoding(source.external_encoding) if source.respond_to?(:external_encoding)
end