Class: Strongbox::Lock

Inherits:
Object
  • Object
show all
Defined in:
lib/strongbox/lock.rb

Overview

The Lock class encrypts and decrypts the protected attribute. It automatically encrypts the data when set and decrypts it when the private key password is provided.

Instance Method Summary collapse

Constructor Details

#initialize(name, instance, options = {}) ⇒ Lock

Returns a new instance of Lock.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/strongbox/lock.rb', line 7

def initialize name, instance, options = {}
  @name              = name
  @instance          = instance

  @size = 0

  options = Strongbox.options.merge(options)

  @base64 = options[:base64]
  @public_key = options[:public_key] || options[:key_pair]
  @private_key = options[:private_key] || options[:key_pair]
  @padding = options[:padding]
  @symmetric = options[:symmetric]
  @symmetric_cipher = options[:symmetric_cipher]
  @symmetric_key = options[:symmetric_key] || "#{name}_key"
  @symmetric_iv = options[:symmetric_iv] || "#{name}_iv"
  @ensure_required_columns = options[:ensure_required_columns]
  @deferred_encryption = options[:deferred_encryption]
end

Instance Method Details

#blank?Boolean

Needed for validations

Returns:

  • (Boolean)


126
127
128
# File 'lib/strongbox/lock.rb', line 126

def blank?
  @raw_content.blank? && @instance[@name].blank?
end

#content(plaintext) ⇒ Object



27
28
29
30
31
32
33
34
# File 'lib/strongbox/lock.rb', line 27

def content plaintext
  @size = plaintext.size unless plaintext.nil? # For validations
  if @deferred_encryption
    @raw_content = plaintext
  else
    encrypt plaintext
  end
end

#decrypt(password = nil, ciphertext = nil) ⇒ Object

Given the private key password decrypts the attribute. Will raise OpenSSL::PKey::RSAError if the password is wrong.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/strongbox/lock.rb', line 77

def decrypt password = nil, ciphertext = nil
  return @raw_content if @deferred_encryption && @raw_content

  # Given a private key and a nil password OpenSSL::PKey::RSA.new() will
  # *prompt* for a password, we default to an empty string to avoid that.
  ciphertext ||= @instance[@name]
  unless @deferred_encryption
    return nil if ciphertext.nil?
    return "" if ciphertext.empty?
  end

  return "*encrypted*" if password.nil?
  unless @private_key
    raise StrongboxError.new("#{@instance.class} model does not have private key_file")
  end

  if ciphertext
    ciphertext = Base64.decode64(ciphertext) if @base64
    private_key = get_rsa_key(@private_key,password)
    if @symmetric == :always
      random_key = @instance[@symmetric_key]
      random_iv = @instance[@symmetric_iv]
      if @base64
        random_key = Base64.decode64(random_key)
        random_iv = Base64.decode64(random_iv)
      end
      cipher = Cipher.new(@symmetric_cipher)
      cipher.decrypt
      cipher.key = private_key.private_decrypt(random_key,@padding)
      cipher.iv = private_key.private_decrypt(random_iv,@padding)
      plaintext = cipher.update(ciphertext)
      plaintext << cipher.final
    else
      plaintext = private_key.private_decrypt(ciphertext,@padding)
    end
  else
    nil
  end
end

#encrypt(plaintext) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/strongbox/lock.rb', line 41

def encrypt plaintext
  ensure_required_columns  if @ensure_required_columns
  unless @public_key
    raise StrongboxError.new("#{@instance.class} model does not have public key_file")
  end
  if !plaintext.blank?
    # Using a blank password in OpenSSL::PKey::RSA.new prevents reading
    # the private key if the file is a key pair
    public_key = get_rsa_key(@public_key,"")
    if @symmetric == :always
      cipher = Cipher.new(@symmetric_cipher)
      cipher.encrypt
      cipher.key = random_key = cipher.random_key
      cipher.iv = random_iv = cipher.random_iv

      ciphertext = cipher.update(plaintext)
      ciphertext << cipher.final
      encrypted_key = public_key.public_encrypt(random_key,@padding)
      encrypted_iv = public_key.public_encrypt(random_iv,@padding)
      if @base64
        encrypted_key = Base64.encode64(encrypted_key)
        encrypted_iv = Base64.encode64(encrypted_iv)
      end
      @instance[@symmetric_key] = encrypted_key
      @instance[@symmetric_iv] = encrypted_iv
    else
      ciphertext = public_key.public_encrypt(plaintext,@padding)
    end
    ciphertext =  Base64.encode64(ciphertext) if @base64
    @instance[@name] = ciphertext
  end
end

#encrypt!Object



36
37
38
39
# File 'lib/strongbox/lock.rb', line 36

def encrypt!
  encrypt @raw_content
  @raw_content = nil
end

#ensure_required_columnsObject



142
143
144
145
146
147
148
149
150
# File 'lib/strongbox/lock.rb', line 142

def ensure_required_columns
  columns = [@name.to_s]
  columns += [@symmetric_key, @symmetric_iv] if @symmetric == :always
  columns.each do |column|
    unless @instance.class.column_names.include? column
      raise StrongboxError.new("#{@instance.class} model does not have database column \"#{column}\"")
    end
  end
end

#lengthObject



138
139
140
# File 'lib/strongbox/lock.rb', line 138

def length
  @size
end

#nil?Boolean

Returns:

  • (Boolean)


130
131
132
# File 'lib/strongbox/lock.rb', line 130

def nil?
  @raw_content.nil? && @instance[@name].nil?
end

#sizeObject



134
135
136
# File 'lib/strongbox/lock.rb', line 134

def size
  @size
end

#to_json(options = nil) ⇒ Object



121
122
123
# File 'lib/strongbox/lock.rb', line 121

def to_json(options = nil)
  to_s
end

#to_sObject



117
118
119
# File 'lib/strongbox/lock.rb', line 117

def to_s
  @raw_content || decrypt
end