Class: Lockbox

Inherits:
Object
  • Object
show all
Defined in:
lib/lockbox/active_storage_extensions.rb,
lib/lockbox.rb,
lib/lockbox/box.rb,
lib/lockbox/utils.rb,
lib/lockbox/aes_gcm.rb,
lib/lockbox/railtie.rb,
lib/lockbox/version.rb,
lib/lockbox/encryptor.rb,
lib/lockbox/carrier_wave_extensions.rb

Overview

ideally encrypt and decrypt would happen at the blob/service level however, there isn’t really a great place to define encryption settings there instead, we encrypt and decrypt at the attachment level, and we define encryption settings at the model level

Defined Under Namespace

Modules: ActiveStorageExtensions, CarrierWaveExtensions Classes: AES_GCM, Box, DecryptionError, Encryptor, Error, Railtie, Utils

Constant Summary collapse

VERSION =
"0.1.1"

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**options) ⇒ Lockbox

Returns a new instance of Lockbox.



20
21
22
23
24
25
26
27
# File 'lib/lockbox.rb', line 20

def initialize(**options)
  options = self.class.default_options.merge(options)
  previous_versions = options.delete(:previous_versions)

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

Class Attribute Details

.default_optionsObject

Returns the value of attribute default_options.



16
17
18
# File 'lib/lockbox.rb', line 16

def default_options
  @default_options
end

Class Method Details

.generate_key_pairObject



59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/lockbox.rb', line 59

def self.generate_key_pair
  require "rbnacl"
  # encryption and decryption servers exchange public keys
  # this produces smaller ciphertext than sealed box
  alice = RbNaCl::PrivateKey.generate
  bob = RbNaCl::PrivateKey.generate
  # alice is sending message to bob
  # use bob first in both cases to prevent keys being swappable
  {
    encryption_key: (bob.public_key.to_bytes + alice.to_bytes).unpack("H*").first,
    decryption_key: (bob.to_bytes + alice.public_key.to_bytes).unpack("H*").first
  }
end

Instance Method Details

#decrypt(ciphertext, **options) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/lockbox.rb', line 34

def decrypt(ciphertext, **options)
  ciphertext = check_string(ciphertext, "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
      error_classes = [DecryptionError]
      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

#encrypt(message, **options) ⇒ Object



29
30
31
32
# File 'lib/lockbox.rb', line 29

def encrypt(message, **options)
  message = check_string(message, "message")
  @boxes.first.encrypt(message, **options)
end