Module: Lockbox
- Extended by:
- Padding
- Defined in:
- lib/lockbox/active_storage_extensions.rb,
lib/lockbox.rb,
lib/lockbox/io.rb,
lib/lockbox/box.rb,
lib/lockbox/model.rb,
lib/lockbox/utils.rb,
lib/lockbox/aes_gcm.rb,
lib/lockbox/padding.rb,
lib/lockbox/railtie.rb,
lib/lockbox/version.rb,
lib/lockbox/migrator.rb,
lib/lockbox/encryptor.rb,
lib/lockbox/calculations.rb,
lib/lockbox/key_generator.rb,
lib/lockbox/log_subscriber.rb,
lib/lockbox/carrier_wave_extensions.rb,
lib/generators/lockbox/audits_generator.rb
Overview
Ideally encryption and decryption would happen at the blob/service level. However, Active Storage < 6.1 only supports a single service (per environment). This means all attachments need to be encrypted or none of them, which is often not practical.
Active Storage 6.1 adds support for multiple services, which changes this. We could have a Lockbox service:
lockbox:
service: Lockbox
backend: local # delegate to another service, like mirror service
key: ... # Lockbox options
However, the checksum is computed *and stored on the blob* before the file is passed to the service. We don’t want the MD5 checksum of the plaintext stored in the database.
Instead, we encrypt and decrypt at the attachment level, and we define encryption settings at the model level.
Defined Under Namespace
Modules: ActiveStorageExtensions, Calculations, CarrierWaveExtensions, Generators, Model, Padding Classes: AES_GCM, Box, DecryptionError, Encryptor, Error, IO, KeyGenerator, LogSubscriber, Migrator, PaddingError, Railtie, Utils
Constant Summary collapse
- VERSION =
"2.0.0"
Constants included from Padding
Padding::PAD_FIRST_BYTE, Padding::PAD_ZERO_BYTE
Class Attribute Summary collapse
-
.default_options ⇒ Object
Returns the value of attribute default_options.
-
.encode_attributes ⇒ Object
Returns the value of attribute encode_attributes.
- .master_key ⇒ Object
Class Method Summary collapse
- .attribute_key(table:, attribute:, master_key: nil, encode: true) ⇒ Object
- .encrypts_action_text_body(**options) ⇒ Object
- .generate_key ⇒ Object
- .generate_key_pair ⇒ Object
- .migrate(relation, batch_size: 1000, restart: false) ⇒ Object
- .new(**options) ⇒ Object
- .rotate(relation, batch_size: 1000, attributes:) ⇒ Object
- .to_hex(str) ⇒ Object
Methods included from Padding
Class Attribute Details
.default_options ⇒ Object
Returns the value of attribute default_options.
30 31 32 |
# File 'lib/lockbox.rb', line 30 def @default_options end |
.encode_attributes ⇒ Object
Returns the value of attribute encode_attributes.
30 31 32 |
# File 'lib/lockbox.rb', line 30 def encode_attributes @encode_attributes end |
.master_key ⇒ Object
36 37 38 |
# File 'lib/lockbox.rb', line 36 def self.master_key @master_key ||= ENV["LOCKBOX_MASTER_KEY"] end |
Class Method Details
.attribute_key(table:, attribute:, master_key: nil, encode: true) ⇒ Object
66 67 68 69 70 71 72 73 |
# File 'lib/lockbox.rb', line 66 def self.attribute_key(table:, attribute:, master_key: nil, encode: true) master_key ||= Lockbox.master_key raise ArgumentError, "Missing master key" unless master_key key = Lockbox::KeyGenerator.new(master_key).attribute_key(table: table, attribute: attribute) key = to_hex(key) if encode key end |
.encrypts_action_text_body(**options) ⇒ Object
83 84 85 86 87 |
# File 'lib/lockbox.rb', line 83 def self.encrypts_action_text_body(**) ActiveSupport.on_load(:action_text_rich_text) do ActionText::RichText.has_encrypted :body, ** end end |
.generate_key ⇒ Object
48 49 50 |
# File 'lib/lockbox.rb', line 48 def self.generate_key SecureRandom.hex(32) end |
.generate_key_pair ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/lockbox.rb', line 52 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: to_hex(bob.public_key.to_bytes + alice.to_bytes), decryption_key: to_hex(bob.to_bytes + alice.public_key.to_bytes) } end |
.migrate(relation, batch_size: 1000, restart: false) ⇒ Object
40 41 42 |
# File 'lib/lockbox.rb', line 40 def self.migrate(relation, batch_size: 1000, restart: false) Migrator.new(relation, batch_size: batch_size).migrate(restart: restart) end |
.new(**options) ⇒ Object
79 80 81 |
# File 'lib/lockbox.rb', line 79 def self.new(**) Encryptor.new(**) end |
.rotate(relation, batch_size: 1000, attributes:) ⇒ Object
44 45 46 |
# File 'lib/lockbox.rb', line 44 def self.rotate(relation, batch_size: 1000, attributes:) Migrator.new(relation, batch_size: batch_size).rotate(attributes: attributes) end |
.to_hex(str) ⇒ Object
75 76 77 |
# File 'lib/lockbox.rb', line 75 def self.to_hex(str) str.unpack1("H*") end |