lib/lockbox.rb in lockbox-0.1.0 vs lib/lockbox.rb in lockbox-0.1.1

- old
+ new

@@ -1,11 +1,8 @@ -# dependencies -require "openssl" -require "securerandom" - # modules require "lockbox/box" +require "lockbox/encryptor" require "lockbox/utils" require "lockbox/version" # integrations require "lockbox/carrier_wave_extensions" if defined?(CarrierWave) @@ -18,39 +15,66 @@ class << self attr_accessor :default_options end self.default_options = {algorithm: "aes-gcm"} - def initialize(key: nil, algorithm: nil, previous_versions: nil) - default_options = self.class.default_options - key ||= default_options[:key] - algorithm ||= default_options[:algorithm] - previous_versions ||= default_options[:previous_versions] + def initialize(**options) + options = self.class.default_options.merge(options) + previous_versions = options.delete(:previous_versions) @boxes = - [Box.new(key, algorithm: algorithm)] + - Array(previous_versions).map { |v| Box.new(v[:key], algorithm: v[:algorithm]) } + [Box.new(options)] + + Array(previous_versions).map { |v| Box.new(v) } end - def encrypt(*args) - @boxes.first.encrypt(*args) + def encrypt(message, **options) + message = check_string(message, "message") + @boxes.first.encrypt(message, **options) end def decrypt(ciphertext, **options) - raise TypeError, "can't convert ciphertext to string" unless ciphertext.respond_to?(:to_str) + ciphertext = check_string(ciphertext, "ciphertext") # ensure binary - ciphertext = ciphertext.to_str 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 DecryptionError, RbNaCl::LengthError, RbNaCl::CryptoError - raise DecryptionError, "Decryption failed" if i == @boxes.size - 1 + 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 + + 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 + + private + + def check_string(str, name) + str = str.read if str.respond_to?(:read) + raise TypeError, "can't convert #{name} to string" unless str.respond_to?(:to_str) + str.to_str end end