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.1.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.
29 30 31 |
# File 'lib/lockbox.rb', line 29 def @default_options end |
.encode_attributes ⇒ Object
Returns the value of attribute encode_attributes.
29 30 31 |
# File 'lib/lockbox.rb', line 29 def encode_attributes @encode_attributes end |
.master_key ⇒ Object
35 36 37 |
# File 'lib/lockbox.rb', line 35 def self.master_key @master_key ||= ENV["LOCKBOX_MASTER_KEY"] end |
Class Method Details
.attribute_key(table:, attribute:, master_key: nil, encode: true) ⇒ Object
65 66 67 68 69 70 71 72 |
# File 'lib/lockbox.rb', line 65 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
82 83 84 85 86 |
# File 'lib/lockbox.rb', line 82 def self.encrypts_action_text_body(**) ActiveSupport.on_load(:action_text_rich_text) do ActionText::RichText.has_encrypted :body, ** end end |
.generate_key ⇒ Object
47 48 49 |
# File 'lib/lockbox.rb', line 47 def self.generate_key SecureRandom.hex(32) end |
.generate_key_pair ⇒ Object
51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/lockbox.rb', line 51 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
39 40 41 |
# File 'lib/lockbox.rb', line 39 def self.migrate(relation, batch_size: 1000, restart: false) Migrator.new(relation, batch_size: batch_size).migrate(restart: restart) end |
.new(**options) ⇒ Object
78 79 80 |
# File 'lib/lockbox.rb', line 78 def self.new(**) Encryptor.new(**) end |
.rotate(relation, batch_size: 1000, attributes:) ⇒ Object
43 44 45 |
# File 'lib/lockbox.rb', line 43 def self.rotate(relation, batch_size: 1000, attributes:) Migrator.new(relation, batch_size: batch_size).rotate(attributes: attributes) end |
.to_hex(str) ⇒ Object
74 75 76 |
# File 'lib/lockbox.rb', line 74 def self.to_hex(str) str.unpack1("H*") end |