encryptor-2.0.0/0000755000004100000410000000000012670216620013570 5ustar www-datawww-dataencryptor-2.0.0/Rakefile0000644000004100000410000000106212670216620015234 0ustar www-datawww-datarequire 'rake' require 'rake/testtask' require 'rdoc/task' require "bundler/gem_tasks" desc 'Test the encryptor gem' Rake::TestTask.new(:test) do |t| t.libs << 'lib' t.pattern = 'test/**/*_test.rb' t.verbose = true end desc 'Generate documentation for the encryptor gem' Rake::RDocTask.new(:rdoc) do |rdoc| rdoc.rdoc_dir = 'rdoc' rdoc.title = 'Encryptor' rdoc.options << '--line-numbers' << '--inline-source' rdoc.rdoc_files.include('README*') rdoc.rdoc_files.include('lib/**/*.rb') end desc 'Default: run unit tests.' task default: :test encryptor-2.0.0/Gemfile0000644000004100000410000000004712670216620015064 0ustar www-datawww-datasource 'https://rubygems.org' gemspec encryptor-2.0.0/MIT-LICENSE0000644000004100000410000000206212670216620015224 0ustar www-datawww-dataCopyright (c) 2011 Sean Huber - shuber@huberry.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.encryptor-2.0.0/data.tar.gz.sig0000444000004100000410000000040012670216620016401 0ustar www-datawww-dataA\D|<2bU PKOt#g'n7˜$Quzʎ%W&9ƶԵϝv0m0HL(PW~sC6e@ s;R83ɋ!q87޵,NyfyvH&w;@J%na٢Ac[3T7BsH/gh+Z7<'<^ҳ)rCV hi55LߑyX}pศ08V;M> encryptor-2.0.0/.travis.yml0000644000004100000410000000051512670216620015702 0ustar www-datawww-datasudo: false language: ruby cache: bundler matrix: fast_finish: true include: - rvm: 2.0.0 - rvm: 2.1 - rvm: 2.2 - rvm: 2.3.0 - rvm: jruby - rvm: rbx allow_failures: - rvm: jruby exclude: - rvm: 1.9.3 addons: code_climate: repo_token: 5dcb75d5b6c58e2a5f6dc850eb2c1d4e0dbf262e69981db00b765a66bfc9ef10 encryptor-2.0.0/lib/0000755000004100000410000000000012670216620014336 5ustar www-datawww-dataencryptor-2.0.0/lib/encryptor.rb0000644000004100000410000001060312670216620016710 0ustar www-datawww-datarequire 'openssl' require 'encryptor/version' # A simple wrapper for the standard OpenSSL library module Encryptor extend self # The default options to use when calling the encrypt and decrypt methods # # Defaults to { algorithm: 'aes-256-gcm', # auth_data: '', # insecure_mode: false, # hmac_iterations: 2000 } # # Run 'openssl list-cipher-commands' in your terminal to view a list all cipher algorithms that are supported on your platform def default_options @default_options ||= { algorithm: 'aes-256-gcm', auth_data: '', insecure_mode: false, hmac_iterations: 2000 } end # Encrypts a :value with a specified :key and :iv. # # Optionally accepts :salt, :auth_data, :algorithm, :hmac_iterations, and :insecure_mode options. # # Example # # encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: 'some secret key', iv: 'some unique value', salt: 'another unique value') # # or # encrypted_value = Encryptor.encrypt('some string to encrypt', key: 'some secret key', iv: 'some unique value', salt: 'another unique value') def encrypt(*args, &block) crypt :encrypt, *args, &block end # Decrypts a :value with a specified :key and :iv. # # Optionally accepts :salt, :auth_data, :algorithm, :hmac_iterations, and :insecure_mode options. # # Example # # decrypted_value = Encryptor.decrypt(value: 'some encrypted string', key: 'some secret key', iv: 'some unique value', salt: 'another unique value') # # or # decrypted_value = Encryptor.decrypt('some encrypted string', key: 'some secret key', iv: 'some unique value', salt: 'another unique value') def decrypt(*args, &block) crypt :decrypt, *args, &block end protected def crypt(cipher_method, *args) #:nodoc: options = default_options.merge(value: args.first).merge(args.last.is_a?(Hash) ? args.last : {}) raise ArgumentError.new('must specify a key') if options[:key].to_s.empty? cipher = OpenSSL::Cipher.new(options[:algorithm]) cipher.send(cipher_method) unless options[:insecure_mode] raise ArgumentError.new("key must be #{cipher.key_len} bytes or longer") if options[:key].bytesize < cipher.key_len raise ArgumentError.new('must specify an iv') if options[:iv].to_s.empty? raise ArgumentError.new("iv must be #{cipher.iv_len} bytes or longer") if options[:iv].bytesize < cipher.iv_len end if options[:iv] cipher.iv = options[:iv] if options[:salt].nil? # Use a non-salted cipher. # This behaviour is retained for backwards compatibility. This mode # is not secure and new deployments should use the :salt options # wherever possible. cipher.key = options[:key] else # Use an explicit salt (which can be persisted into a database on a # per-column basis, for example). This is the preferred (and more # secure) mode of operation. cipher.key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(options[:key], options[:salt], options[:hmac_iterations], cipher.key_len) end else # This is deprecated and needs to be changed. cipher.pkcs5_keyivgen(options[:key]) end yield cipher, options if block_given? value = options[:value] if cipher.authenticated? if encryption?(cipher_method) cipher.auth_data = options[:auth_data] else value = extract_cipher_text(options[:value]) cipher.auth_tag = extract_auth_tag(options[:value]) # auth_data must be set after auth_tag has been set when decrypting # See http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-auth_data-3D cipher.auth_data = options[:auth_data] end end result = cipher.update(value) result << cipher.final result << cipher.auth_tag if cipher.authenticated? && encryption?(cipher_method) result end def encryption?(cipher_method) cipher_method == :encrypt end def extract_cipher_text(value) value[0..-17] end def extract_auth_tag(value) value[-16..-1] end end encryptor-2.0.0/lib/encryptor/0000755000004100000410000000000012670216620016363 5ustar www-datawww-dataencryptor-2.0.0/lib/encryptor/string.rb0000644000004100000410000000132412670216620020216 0ustar www-datawww-datamodule Encryptor # Adds encrypt and decrypt methods to strings module String # Returns a new string containing the encrypted version of itself def encrypt(options = {}) Encryptor.encrypt(options.merge(value: self)) end # Replaces the contents of a string with the encrypted version of itself def encrypt!(options ={}) replace encrypt(options) end # Returns a new string containing the decrypted version of itself def decrypt(options = {}) Encryptor.decrypt(options.merge(value: self)) end # Replaces the contents of a string with the decrypted version of itself def decrypt!(options = {}) replace decrypt(options) end end endencryptor-2.0.0/lib/encryptor/version.rb0000644000004100000410000000056112670216620020377 0ustar www-datawww-datamodule Encryptor # Contains information about this gem's version module Version MAJOR = 2 MINOR = 0 PATCH = 0 # Returns a version string by joining MAJOR, MINOR, and PATCH with '.' # # Example # # Version.to_s # '1.0.2' def self.to_s [MAJOR, MINOR, PATCH].join('.') end end end encryptor-2.0.0/test/0000755000004100000410000000000012670216620014547 5ustar www-datawww-dataencryptor-2.0.0/test/encryptor_string_test.rb0000644000004100000410000000601212670216620021545 0ustar www-datawww-datarequire 'test_helper' class EncryptorStringTest < Minitest::Test original_value = StringWithEncryptor.new key = SecureRandom.random_bytes(64) iv = SecureRandom.random_bytes(64) salt = Time.now.to_i.to_s original_value << SecureRandom.random_bytes(64) auth_data = SecureRandom.random_bytes(64) wrong_auth_tag = SecureRandom.random_bytes(16) OpenSSLHelper::ALGORITHMS.each do |algorithm| encrypted_value_with_iv = StringWithEncryptor.new encrypted_value_without_iv = StringWithEncryptor.new encrypted_value_with_iv << Encryptor.encrypt(value: original_value, key: key, iv: iv, salt: salt, algorithm: algorithm) encrypted_value_without_iv << Encryptor.encrypt(value: original_value, key: key, algorithm: algorithm, insecure_mode: true) define_method "test_should_call_encrypt_on_a_string_with_the_#{algorithm}_algorithm_with_iv" do assert_equal encrypted_value_with_iv, original_value.encrypt(key: key, iv: iv, salt: salt, algorithm: algorithm) end define_method "test_should_call_encrypt_on_a_string_with_the_#{algorithm}_algorithm_without_iv" do assert_equal encrypted_value_without_iv, original_value.encrypt(key: key, algorithm: algorithm, insecure_mode: true) end define_method "test_should_call_decrypt_on_a_string_with_the_#{algorithm}_algorithm_with_iv" do assert_equal original_value, encrypted_value_with_iv.decrypt(key: key, iv: iv, salt: salt, algorithm: algorithm) end define_method "test_should_call_decrypt_on_a_string_with_the_#{algorithm}_algorithm_without_iv" do assert_equal original_value, encrypted_value_without_iv.decrypt(key: key, algorithm: algorithm, insecure_mode: true) end define_method "test_string_encrypt!_on_a_string_with_the_#{algorithm}_algorithm_with_iv" do original_value_dup = original_value.dup original_value_dup.encrypt!(key: key, iv: iv, salt: salt, algorithm: algorithm) assert_equal original_value.encrypt(key: key, iv: iv, salt: salt, algorithm: algorithm), original_value_dup end define_method "test_string_encrypt!_on_a_string_with_the_#{algorithm}_algorithm_without_iv" do original_value_dup = original_value.dup original_value_dup.encrypt!(key: key, algorithm: algorithm, insecure_mode: true) assert_equal original_value.encrypt(key: key, algorithm: algorithm, insecure_mode: true), original_value_dup end define_method "test_string_decrypt!_on_a_string_with_the_#{algorithm}_algorithm_with_iv" do encrypted_value_with_iv_dup = encrypted_value_with_iv.dup encrypted_value_with_iv_dup.decrypt!(key: key, iv: iv, salt: salt, algorithm: algorithm) assert_equal original_value, encrypted_value_with_iv_dup end define_method "test_string_decrypt!_on_a_string_with_the_#{algorithm}_algorithm_without_iv" do encrypted_value_without_iv_dup = encrypted_value_without_iv.dup encrypted_value_without_iv_dup.decrypt!(key: key, algorithm: algorithm, insecure_mode: true) assert_equal original_value, encrypted_value_without_iv_dup end end end encryptor-2.0.0/test/test_helper.rb0000644000004100000410000000120712670216620017412 0ustar www-datawww-datarequire 'simplecov' require 'simplecov-rcov' require "codeclimate-test-reporter" SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ SimpleCov::Formatter::HTMLFormatter, SimpleCov::Formatter::RcovFormatter, CodeClimate::TestReporter::Formatter ] SimpleCov.start do add_filter 'test' end CodeClimate::TestReporter.start require 'minitest/autorun' require 'minitest/unit' require 'digest/sha2' $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) $:.unshift(File.dirname(__FILE__)) require 'encryptor' require 'openssl_helper' require 'encryptor/string' class StringWithEncryptor < String include Encryptor::String end encryptor-2.0.0/test/openssl_helper.rb0000644000004100000410000000171512670216620020122 0ustar www-datawww-datamodule OpenSSLHelper # For test debugging puts "OpenSSL Version: #{OpenSSL::OPENSSL_VERSION}" algorithms = OpenSSL::Cipher.ciphers case RUBY_PLATFORM.to_sym when :java security_class = java.lang.Class.for_name('javax.crypto.JceSecurity') restricted_field = security_class.get_declared_field('isRestricted') restricted_field.accessible = true restricted_field.set(nil, false) # if key length is less than 24 bytes: # OpenSSL::Cipher::CipherError: key length too short # if key length is 24 bytes or more: # OpenSSL::Cipher::CipherError: DES key too long - should be 8 bytes: possibly you need to install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for your JRE algorithms -= %w(des-ede3 DES-EDE3) else algorithms &= %x(openssl list-cipher-commands).split end ALGORITHMS = algorithms.freeze AUTHENTICATED_ENCRYPTION_ALGORITHMS = ['aes-128-gcm','aes-192-gcm','aes-256-gcm'] end encryptor-2.0.0/test/legacy_encryptor_string_test.rb0000644000004100000410000000571412670216620023101 0ustar www-datawww-datarequire 'test_helper' class LegacyEncryptorStringTest < Minitest::Test key = SecureRandom.random_bytes(64) iv = SecureRandom.random_bytes(64) original_value = StringWithEncryptor.new original_value << SecureRandom.random_bytes(64) OpenSSLHelper::ALGORITHMS.each do |algorithm| encrypted_value_with_iv = StringWithEncryptor.new encrypted_value_with_iv << Encryptor.encrypt(value: original_value, key: key, iv: iv, algorithm: algorithm, insecure_mode: true) encrypted_value_without_iv = StringWithEncryptor.new encrypted_value_without_iv << Encryptor.encrypt(value: original_value, key: key, algorithm: algorithm, insecure_mode: true) define_method "test_should_call_encrypt_on_a_string_with_the_#{algorithm}_algorithm_with_iv" do assert_equal encrypted_value_with_iv, original_value.encrypt(key: key, iv: iv, algorithm: algorithm, insecure_mode: true) end define_method "test_should_call_encrypt_on_a_string_with_the_#{algorithm}_algorithm_without_iv" do assert_equal encrypted_value_without_iv, original_value.encrypt(key: key, algorithm: algorithm, insecure_mode: true) end define_method "test_should_call_decrypt_on_a_string_with_the_#{algorithm}_algorithm_with_iv" do assert_equal original_value, encrypted_value_with_iv.decrypt(key: key, iv: iv, algorithm: algorithm, insecure_mode: true) end define_method "test_should_call_decrypt_on_a_string_with_the_#{algorithm}_algorithm_without_iv" do assert_equal original_value, encrypted_value_without_iv.decrypt(key: key, algorithm: algorithm, insecure_mode: true) end define_method "test_string_encrypt!_on_a_string_with_the_#{algorithm}_algorithm_with_iv" do original_value_dup = original_value.dup original_value_dup.encrypt!(key: key, iv: iv, algorithm: algorithm, insecure_mode: true) assert_equal original_value.encrypt(key: key, iv: iv, algorithm: algorithm, insecure_mode: true), original_value_dup end define_method "test_string_encrypt!_on_a_string_with_the_#{algorithm}_algorithm_without_iv" do original_value_dup = original_value.dup original_value_dup.encrypt!(key: key, algorithm: algorithm, insecure_mode: true) assert_equal original_value.encrypt(key: key, algorithm: algorithm, insecure_mode: true), original_value_dup end define_method "test_string_decrypt!_on_a_string_with_the_#{algorithm}_algorithm_with_iv" do encrypted_value_with_iv_dup = encrypted_value_with_iv.dup encrypted_value_with_iv_dup.decrypt!(key: key, iv: iv, algorithm: algorithm, insecure_mode: true) assert_equal original_value, encrypted_value_with_iv_dup end define_method "test_string_decrypt!_on_a_string_with_the_#{algorithm}_algorithm_without_iv" do encrypted_value_without_iv_dup = encrypted_value_without_iv.dup encrypted_value_without_iv_dup.decrypt!(key: key, algorithm: algorithm, insecure_mode: true) assert_equal original_value, encrypted_value_without_iv_dup end end end encryptor-2.0.0/test/encryptor_test.rb0000644000004100000410000001267612670216620020174 0ustar www-datawww-datarequire 'test_helper' # Tests for new preferred salted encryption mode # class EncryptorTest < Minitest::Test key = SecureRandom.random_bytes(32) iv = SecureRandom.random_bytes(16) salt = SecureRandom.random_bytes(16) original_value = SecureRandom.random_bytes(64) auth_data = SecureRandom.random_bytes(64) wrong_auth_tag = SecureRandom.random_bytes(16) OpenSSLHelper::ALGORITHMS.each do |algorithm| encrypted_value_with_iv = Encryptor.encrypt(value: original_value, key: key, iv: iv, salt: salt, algorithm: algorithm) encrypted_value_without_iv = Encryptor.encrypt(value: original_value, key: key, algorithm: algorithm, insecure_mode: true) define_method "test_should_crypt_with_the_#{algorithm}_algorithm_with_iv" do refute_equal original_value, encrypted_value_with_iv refute_equal encrypted_value_without_iv, encrypted_value_with_iv assert_equal original_value, Encryptor.decrypt(value: encrypted_value_with_iv, key: key, iv: iv, salt: salt, algorithm: algorithm) end define_method "test_should_crypt_with_the_#{algorithm}_algorithm_without_iv" do refute_equal original_value, encrypted_value_without_iv assert_equal original_value, Encryptor.decrypt(value: encrypted_value_without_iv, key: key, algorithm: algorithm, insecure_mode: true) end define_method "test_should_encrypt_with_the_#{algorithm}_algorithm_with_iv_with_the_first_arg_as_the_value" do assert_equal encrypted_value_with_iv, Encryptor.encrypt(original_value, key: key, iv: iv, salt: salt, algorithm: algorithm) end define_method "test_should_encrypt_with_the_#{algorithm}_algorithm_without_iv_with_the_first_arg_as_the_value" do assert_equal encrypted_value_without_iv, Encryptor.encrypt(original_value, key: key, algorithm: algorithm, insecure_mode: true) end define_method "test_should_decrypt_with_the_#{algorithm}_algorithm_with_iv_with_the_first_arg_as_the_value" do assert_equal original_value, Encryptor.decrypt(encrypted_value_with_iv, key: key, iv: iv, salt: salt, algorithm: algorithm) end define_method "test_should_decrypt_with_the_#{algorithm}_algorithm_without_iv_with_the_first_arg_as_the_value" do assert_equal original_value, Encryptor.decrypt(encrypted_value_without_iv, key: key, algorithm: algorithm, insecure_mode: true) end end define_method 'test_should_use_the_default_algorithm_if_one_is_not_specified' do assert_equal Encryptor.encrypt(value: original_value, key: key, salt: salt, iv: iv, algorithm: Encryptor.default_options[:algorithm]), Encryptor.encrypt(value: original_value, key: key, salt: salt, iv: iv) end def test_should_have_a_default_algorithm assert !Encryptor.default_options[:algorithm].nil? assert !Encryptor.default_options[:algorithm].empty? end def test_should_raise_argument_error_if_key_is_not_specified assert_raises(ArgumentError, "must specify a key") { Encryptor.encrypt('some value') } assert_raises(ArgumentError, "must specify a key") { Encryptor.decrypt('some encrypted string') } end def test_should_raise_argument_error_if_key_is_too_short assert_raises(ArgumentError, "key must be 32 bytes or longer") { Encryptor.encrypt('some value', key: '') } assert_raises(ArgumentError, "key must be 32 bytes or longer") { Encryptor.decrypt('some encrypted string', key: '') } end define_method 'test_should_raise_argument_error_if_iv_is_not_specified' do assert_raises(ArgumentError, "must specify an iv") { Encryptor.encrypt('some value', key: key) } assert_raises(ArgumentError, "must specify an iv") { Encryptor.decrypt('some encrypted string', key: key) } end define_method 'test_should_raise_argument_error_if_iv_is_too_short' do assert_raises(ArgumentError, "iv must be 16 bytes or longer") { Encryptor.encrypt('some value', key: key, iv: 'a') } assert_raises(ArgumentError, "iv must be 16 bytes or longer") { Encryptor.decrypt('some encrypted string', key: key, iv: 'a') } end define_method 'test_should_yield_block_with_cipher_and_options' do called = false Encryptor.encrypt('some value', key: key, iv: iv, salt: salt) { |cipher, options| called = true } assert called end OpenSSLHelper::AUTHENTICATED_ENCRYPTION_ALGORITHMS.each do |algorithm| define_method 'test_should_use_the_default_authentication_data_if_it_is_not_specified' do encrypted_value = Encryptor.encrypt(value: original_value, key: key, iv: iv, salt: salt, algorithm: algorithm) decrypted_value = Encryptor.decrypt(value: encrypted_value, key: key, iv: iv, salt: salt, algorithm: algorithm) refute_equal original_value, encrypted_value assert_equal original_value, decrypted_value assert_raises(OpenSSL::Cipher::CipherError) { Encryptor.decrypt(value: encrypted_value[0..-17] + wrong_auth_tag, key: key, iv: iv, salt: salt, algorithm: algorithm) } end define_method 'test_should_use_authentication_data_if_it_is_specified' do encrypted_value = Encryptor.encrypt(value: original_value, key: key, iv: iv, salt: salt, algorithm: algorithm, auth_data: auth_data) decrypted_value = Encryptor.decrypt(value: encrypted_value, key: key, iv: iv, salt: salt, algorithm: algorithm, auth_data: auth_data) refute_equal original_value, encrypted_value assert_equal original_value, decrypted_value assert_raises(OpenSSL::Cipher::CipherError) { Encryptor.decrypt(value: encrypted_value[0..-17] + wrong_auth_tag, key: key, iv: iv, salt: salt, algorithm: algorithm) } end end end encryptor-2.0.0/test/legacy_encryptor_test.rb0000644000004100000410000000652512670216620021514 0ustar www-datawww-datarequire 'test_helper' # Tests for legacy (non-salted) encryption mode # class LegacyEncryptorTest < Minitest::Test key = SecureRandom.random_bytes(64) iv = SecureRandom.random_bytes(64) original_value = SecureRandom.random_bytes(64) OpenSSLHelper::ALGORITHMS.each do |algorithm| encrypted_value_with_iv = Encryptor.encrypt(value: original_value, key: key, iv: iv, algorithm: algorithm, insecure_mode: true) encrypted_value_without_iv = Encryptor.encrypt(value: original_value, key: key, algorithm: algorithm, insecure_mode: true) define_method "test_should_crypt_with_the_#{algorithm}_algorithm_with_iv" do refute_equal original_value, encrypted_value_with_iv refute_equal encrypted_value_without_iv, encrypted_value_with_iv assert_equal original_value, Encryptor.decrypt(value: encrypted_value_with_iv, key: key, iv: iv, algorithm: algorithm, insecure_mode: true) end define_method "test_should_crypt_with_the_#{algorithm}_algorithm_without_iv" do refute_equal original_value, encrypted_value_without_iv assert_equal original_value, Encryptor.decrypt(value: encrypted_value_without_iv, key: key, algorithm: algorithm, insecure_mode: true) end define_method "test_should_encrypt_with_the_#{algorithm}_algorithm_with_iv_with_the_first_arg_as_the_value" do assert_equal encrypted_value_with_iv, Encryptor.encrypt(original_value, key: key, iv: iv, algorithm: algorithm, insecure_mode: true) end define_method "test_should_encrypt_with_the_#{algorithm}_algorithm_without_iv_with_the_first_arg_as_the_value" do assert_equal encrypted_value_without_iv, Encryptor.encrypt(original_value, key: key, algorithm: algorithm, insecure_mode: true) end define_method "test_should_decrypt_with_the_#{algorithm}_algorithm_with_iv_with_the_first_arg_as_the_value" do assert_equal original_value, Encryptor.decrypt(encrypted_value_with_iv, key: key, iv: iv, algorithm: algorithm, insecure_mode: true) end define_method "test_should_decrypt_with_the_#{algorithm}_algorithm_without_iv_with_the_first_arg_as_the_value" do assert_equal original_value, Encryptor.decrypt(encrypted_value_without_iv, key: key, algorithm: algorithm, insecure_mode: true) end end define_method 'test_should_use_the_default_algorithm_if_one_is_not_specified' do assert_equal Encryptor.encrypt(value: original_value, key: key, algorithm: Encryptor.default_options[:algorithm], insecure_mode: true), Encryptor.encrypt(value: original_value, key: key, insecure_mode: true) end def test_should_have_a_default_algorithm assert !Encryptor.default_options[:algorithm].nil? assert !Encryptor.default_options[:algorithm].empty? end def test_should_raise_argument_error_if_key_is_not_specified assert_raises(ArgumentError) { Encryptor.encrypt('some value', insecure_mode: true) } assert_raises(ArgumentError) { Encryptor.decrypt('some encrypted string', insecure_mode: true) } assert_raises(ArgumentError) { Encryptor.encrypt('some value', key: '', insecure_mode: true) } assert_raises(ArgumentError) { Encryptor.decrypt('some encrypted string', key: '', insecure_mode: true) } end def test_should_yield_block_with_cipher_and_options called = false Encryptor.encrypt('some value', key: 'some key', insecure_mode: true) { |cipher, options| called = true } assert called end end encryptor-2.0.0/test/compatibility_test.rb0000644000004100000410000000543212670216620021010 0ustar www-datawww-datarequire File.expand_path('../test_helper', __FILE__) # Test ensures that values stored by previous versions of the gem will # roundtrip and decrypt correctly in this and future versions. This is important # for data stored in databases and allows consumers of the gem to upgrade with # confidence in the future. # class CompatibilityTest < Minitest::Test ALGORITHM = 'aes-256-cbc' def self.base64_encode(value) [value].pack('m').strip end def self.base64_decode(value) value.unpack('m').first end if OpenSSL::Cipher.ciphers.include?(ALGORITHM) def test_encrypt_with_iv key = Digest::SHA256.hexdigest('my-fixed-key') iv = Digest::SHA256.hexdigest('my-fixed-iv') result = Encryptor.encrypt( algorithm: ALGORITHM, value: 'my-fixed-input', key: key, iv: iv, insecure_mode: true ) assert_equal 'nGuyGniksFXnMYj/eCxXKQ==', self.class.base64_encode(result) end def test_encrypt_without_iv key = Digest::SHA256.hexdigest('my-fixed-key') result = Encryptor.encrypt( algorithm: ALGORITHM, value: 'my-fixed-input', key: key, insecure_mode: true ) assert_equal 'XbwHRMFWqR5M80kgwRcEEg==', self.class.base64_encode(result) end def test_decrypt_with_iv key = Digest::SHA256.hexdigest('my-fixed-key') iv = Digest::SHA256.hexdigest('my-fixed-iv') result = Encryptor.decrypt( algorithm: ALGORITHM, value: self.class.base64_decode('nGuyGniksFXnMYj/eCxXKQ=='), key: key, iv: iv, insecure_mode: true ) assert_equal 'my-fixed-input', result end def test_decrypt_without_iv key = Digest::SHA256.hexdigest('my-fixed-key') result = Encryptor.decrypt( algorithm: ALGORITHM, value: self.class.base64_decode('XbwHRMFWqR5M80kgwRcEEg=='), key: key, insecure_mode: true ) assert_equal 'my-fixed-input', result end def test_encrypt_with_iv_and_salt key = Digest::SHA256.hexdigest('my-fixed-key') iv = Digest::SHA256.hexdigest('my-fixed-iv') salt = 'my-fixed-salt' result = Encryptor.encrypt( algorithm: ALGORITHM, value: 'my-fixed-input', key: key, iv: iv, salt: salt ) assert_equal 'DENuQSh9b0eW8GN3YLzLGw==', self.class.base64_encode(result) end def test_decrypt_with_iv_and_salt key = Digest::SHA256.hexdigest('my-fixed-key') iv = Digest::SHA256.hexdigest('my-fixed-iv') salt = 'my-fixed-salt' result = Encryptor.decrypt( algorithm: ALGORITHM, value: self.class.base64_decode('DENuQSh9b0eW8GN3YLzLGw=='), key: key, iv: iv, salt: salt ) assert_equal 'my-fixed-input', result end end end encryptor-2.0.0/encryptor.gemspec0000644000004100000410000000267112670216620017170 0ustar www-datawww-data# -*- encoding: utf-8 -*- lib = File.expand_path('../lib/', __FILE__) $:.unshift lib unless $:.include?(lib) require 'encryptor/version' require 'date' Gem::Specification.new do |s| s.name = 'encryptor' s.version = Encryptor::Version s.date = Date.today s.platform = Gem::Platform::RUBY s.summary = 'A simple wrapper for the standard ruby OpenSSL library' s.description = 'A simple wrapper for the standard ruby OpenSSL library to encrypt and decrypt strings' s.authors = ['Sean Huber', 'S. Brent Faulkner', 'William Monk', 'Stephen Aghaulor'] s.email = ['sean@shuber.io', 'sbfaulkner@gmail.com', 'billy.monk@gmail.com', 'saghaulor@gmail.com'] s.homepage = 'http://github.com/attr-encrypted/encryptor' s.license = 'MIT' s.rdoc_options = %w(--charset=UTF-8 --inline-source --line-numbers --main README.md) s.require_paths = ['lib'] s.files = `git ls-files`.split("\n") s.test_files = `git ls-files -- test/*`.split("\n") s.required_ruby_version = '>= 2.0.0' s.add_development_dependency('minitest', '>= 0') s.add_development_dependency('rake', '>= 0') s.add_development_dependency('simplecov', '>= 0') s.add_development_dependency('simplecov-rcov', '>= 0') s.add_development_dependency('codeclimate-test-reporter', '>= 0') s.requirements << 'openssl, >= v1.0.1' s.cert_chain = ['certs/saghaulor.pem'] s.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/ end encryptor-2.0.0/.gitignore0000644000004100000410000000004712670216620015561 0ustar www-datawww-datapkg rdoc .bundle coverage Gemfile.lock encryptor-2.0.0/checksums.yaml.gz.sig0000444000004100000410000000040012670216620017631 0ustar www-datawww-data~=>AR2.xl_'E*cfLgƸJv-w'{*kV[ qX4ׁ(9&?ɛr<,ƻ6_<Ժp>R 4a\d7oe7¦ F`0[9Y6N)P?NS9CIS.R1R .z'![ 3w1F8 k4T+S V~`kFqencryptor-2.0.0/certs/0000755000004100000410000000000012670216620014710 5ustar www-datawww-dataencryptor-2.0.0/certs/saghaulor.pem0000644000004100000410000000235112670216620017401 0ustar www-datawww-data-----BEGIN CERTIFICATE----- MIIDdDCCAlygAwIBAgIBATANBgkqhkiG9w0BAQUFADBAMRIwEAYDVQQDDAlzYWdo YXVsb3IxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixkARkWA2Nv bTAeFw0xNjAxMTEyMjQyMDFaFw0xNzAxMTAyMjQyMDFaMEAxEjAQBgNVBAMMCXNh Z2hhdWxvcjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYD Y29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx0xdQYk2GwCpQ1n/ n2mPVYHLYqU5TAn/82t5kbqBUWjbcj8tHAi41tJ19+fT/hH0dog8JHvho1zmOr71 ZIqreJQo60TqP6oE9a5HncUpjqbRp7tOmHo9E+mOW1yT4NiXqFf1YINExQKy2XND WPQ+T50ZNUsGMfHFWB4NAymejRWXlOEY3bvKW0UHFeNmouP5he51TjoP8uCc9536 4AIWVP/zzzjwrFtC7av7nRw4Y+gX2bQjrkK2k2JS0ejiGzKBIEMJejcI2B+t79zT kUQq9SFwp2BrKSIy+4kh4CiF20RT/Hfc1MbvTxSIl/bbIxCYEOhmtHExHi0CoCWs YCGCXQIDAQABo3kwdzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQU SCpVzSBvYbO6B3oT3n3RCZmurG8wHgYDVR0RBBcwFYETc2FnaGF1bG9yQGdtYWls LmNvbTAeBgNVHRIEFzAVgRNzYWdoYXVsb3JAZ21haWwuY29tMA0GCSqGSIb3DQEB BQUAA4IBAQAeiGdC3e0WiZpm0cF/b7JC6hJYXC9Yv9VsRAWD9ROsLjFKwOhmonnc +l/QrmoTjMakYXBCai/Ca3L+k5eRrKilgyITILsmmFxK8sqPJXUw2Jmwk/dAky6x hHKVZAofT1OrOOPJ2USoZyhR/VI8epLaD5wUmkVDNqtZWviW+dtRa55aPYjRw5Pj wuj9nybhZr+BbEbmZE//2nbfkM4hCuMtxxxilPrJ22aYNmeWU0wsPpDyhPYxOUgU ZjeLmnSDiwL6doiP5IiwALH/dcHU67ck3NGf6XyqNwQrrmtPY0mv1WVVL4Uh+vYE kHoFzE2no0BfBg78Re8fY69P5yES5ncC -----END CERTIFICATE----- encryptor-2.0.0/CHANGELOG.md0000644000004100000410000000257212670216620015407 0ustar www-datawww-data# Encryptor # ## Unreleased ## * Added support for MRI 2.1, 2.2, 2.3, and Rubinius. (@saghaulor) * Added support for Authenticated Encryption Authentiation Data (AEAD) via aes-###-gcm. (@saghaulor) * Changed the defaults to improve security, aes-256-gcm, IV is required. (@saghaulor) * Added key and IV minimum length validations. (@saghaulor) * Added insecure_mode option to allow for backwards compatibility for users who didn't use unique IV. (@saghaulor) * Deprecated using Encryptor without an IV. * Added hmac_iterations option to allow for adjusting the number of PKCS5 iterations when deriving a unique key. (@saghaulor) * Removed support for MRI 1.9.3 and JRuby (until JRuby supports `auth_data=`, https://github.com/jruby/jruby/issues/3376). (@saghaulor) * Changed tests to use Minitest. (@saghaulor) * Changed syntax to use Ruby 1.9+ hash syntax. (@saghaulor) * Salt may be deprecated in a future release, it remains for backwards compatibility. It's better security to have a unique key per record, however, the cost of PKCS5 is too high to force on to users by default. If users want a unique key per record they can implement it in their own way. ## 1.3.0 ## * Added support for unique key (via salt) and IV. (@danpal & @rcook) ## 1.2.3 ## * Added support for passing blocks to `encrypt` and `decrypt`. (@shuber) * Changed raising an exception if key is missing or empty. (@shuber) encryptor-2.0.0/metadata.gz.sig0000444000004100000410000000040012670216620016463 0ustar www-datawww-data!ZN/l<;4~zV2 38)V7v5 *`,_G8-_>OWMHGf!\UDF2G' Fe`r6$vX*%P{n`csfOvTh0 8툿Y'V֡zєN/CK8O$[,26t#w~Rz5_p䈠߹И4 w)$RXencryptor-2.0.0/README.md0000644000004100000410000001641512670216620015056 0ustar www-datawww-data# Encryptor [![Build Status](https://secure.travis-ci.org/attr-encrypted/encryptor.svg)](https://travis-ci.org/attr-encrypted/encryptor) [![Code Climate](https://codeclimate.com/github/attr-encrypted/encryptor/badges/gpa.svg)](https://codeclimate.com/github/attr-encrypted/encryptor) [![Coverage](https://codeclimate.com/github/attr-encrypted/encryptor/badges/coverage.svg)](https://codeclimate.com/github/attr-encrypted/encryptor) [![Gem Version](https://badge.fury.io/rb/encryptor.svg)](http://badge.fury.io/rb/encryptor) [![security](https://hakiri.io/github/attr-encrypted/encryptor/master.svg)](https://hakiri.io/github/attr-encrypted/encryptor/master) A simple wrapper for the standard Ruby OpenSSL library ### Installation ```bash gem install encryptor ``` ### Usage #### Basic Encryptor uses the AES-256-GCM algorithm by default to encrypt strings securely. The best example is: ```ruby cipher = OpenSSL::Cipher.new('aes-256-gcm') cipher.encrypt # Required before '#random_key' or '#random_iv' can be called. http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/Cipher.html#method-i-encrypt secret_key = cipher.random_key # Insures that the key is the correct length respective to the algorithm used. iv = cipher.random_iv # Insures that the IV is the correct length respective to the algorithm used. salt = SecureRandom.random_bytes(16) encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: secret_key, iv: iv, salt: salt) decrypted_value = Encryptor.decrypt(value: encrypted_value, key: secret_key, iv: iv, salt: salt) ``` A slightly easier example is: ```ruby require 'securerandom' secret_key = SecureRandom.random_bytes(32) # The length in bytes must be equal to or greater than the algorithm bit length. iv = SecureRandom.random_bytes(12) # Recomended length for AES-###-GCM algorithm. https://tools.ietf.org/html/rfc5084#section-3.2 encrypted_value = Encryptor.encrypt(value: 'some string to encrypt', key: secret_key, iv: iv) decrypted_value = Encryptor.decrypt(value: encrypted_value, key: secret_key, iv: iv) ``` **NOTE: It is imperative that you use a unique IV per each string and encryption key combo; a nonce as the IV.** See [RFC 5084](https://tools.ietf.org/html/rfc5084#section-1.5) for more details. The value to encrypt or decrypt may also be passed as the first option if you'd prefer. ```ruby encrypted_value = Encryptor.encrypt('some string to encrypt', key: secret_key, iv: iv) decrypted_value = Encryptor.decrypt(encrypted_value, key: secret_key, iv: iv) ``` #### Options **Defaults:** ```ruby { algorithm: 'aes-256-gcm', auth_data: '', insecure_mode: false, hmac_iterations: 2000 } ``` Older versions of Encryptor allowed you to use it in a less secure way. Namely, you were allowed to run Encryptor without an IV, or with a key of insufficient length. Encryptor now requires a key and IV of the correct length respective to the algorithm that you use. However, to maintain backwards compatibility you can run Encryptor with the `:insecure_mode` option. You may also pass an `:algorithm`,`:salt`, and `hmac_iterations` option, however none of these options are required. If you pass the `:salt` option, a new unique key will be derived from the key that you passed in using PKCS5 with a default of 2000 iterations. You can change the number of PKCS5 iterations with the `hmac_iterations` option. As PKCS5 is slow, it is optional behavior, but it does provide more security to use a unique IV and key for every encryption operation. ```ruby Encryptor.default_options.merge!(algorithm: 'aes-256-cbc', key: 'some default secret key', iv: iv, salt: salt) ``` #### Strings Older versions of Encryptor added `encrypt` and `decrypt` methods to `String` objects for your convenience. However, this behavior has been removed to avoid polluting Ruby's core `String` class. The `Encryptor::String` module remains within this gem to allow users of this feature to implement it themselves. These `encrypt` and `decrypt` methods accept the same arguments as the associated ones in the `Encryptor` module. They're nice when you set the default options in the `Encryptor.default_options attribute.` For example: ```ruby require 'encryptor/string' String.include Encryptor::String Encryptor.default_options.merge!(key: 'some default secret key', iv: iv) credit_card = 'xxxx xxxx xxxx 1234' encrypted_credit_card = credit_card.encrypt ``` There's also `encrypt!` and `decrypt!` methods that replace the contents of a string with the encrypted or decrypted version of itself. #### Algorithms To view a list of all cipher algorithms that are supported on your platform, run the following code in your favorite Ruby REPL: ```ruby require 'openssl' puts OpenSSL::Cipher.ciphers ``` The supported ciphers will vary depending on the version of OpenSSL that was used to compile your version of Ruby. However, the following ciphers are typically supported: Cipher Name|Key size in bytes|IV size in bytes ---|---|--- aes-128-cbc|16|16 aes-128-cbc-hmac-sha1|16|16 aes-128-cbc-hmac-sha256|16|16 aes-128-ccm|16|12 aes-128-cfb|16|16 aes-128-cfb1|16|16 aes-128-cfb8|16|16 aes-128-ctr|16|16 aes-128-ecb|16|0 aes-128-gcm|16|12 aes-128-ofb|16|16 aes-128-xts|32|16 aes-192-cbc|24|16 aes-192-ccm|24|12 aes-192-cfb|24|16 aes-192-cfb1|24|16 aes-192-cfb8|24|16 aes-192-ctr|24|16 aes-192-ecb|24|0 aes-192-gcm|24|12 aes-192-ofb|24|16 aes-256-cbc|32|16 aes-256-cbc-hmac-sha1|32|16 aes-256-cbc-hmac-sha256|32|16 aes-256-ccm|32|12 aes-256-cfb|32|16 aes-256-cfb1|32|16 aes-256-cfb8|32|16 aes-256-ctr|32|16 aes-256-ecb|32|0 aes-256-gcm|32|12 aes-256-ofb|32|16 aes-256-xts|64|16 aes128|16|16 aes192|24|16 aes256|32|16 bf|16|8 bf-cbc|16|8 bf-cfb|16|8 bf-ecb|16|0 bf-ofb|16|8 blowfish|16|8 camellia-128-cbc|16|16 camellia-128-cfb|16|16 camellia-128-cfb1|16|16 camellia-128-cfb8|16|16 camellia-128-ecb|16|0 camellia-128-ofb|16|16 camellia-192-cbc|24|16 camellia-192-cfb|24|16 camellia-192-cfb1|24|16 camellia-192-cfb8|24|16 camellia-192-ecb|24|0 camellia-192-ofb|24|16 camellia-256-cbc|32|16 camellia-256-cfb|32|16 camellia-256-cfb1|32|16 camellia-256-cfb8|32|16 camellia-256-ecb|32|0 camellia-256-ofb|32|16 camellia128|16|16 camellia192|24|16 camellia256|32|16 cast|16|8 cast-cbc|16|8 cast5-cbc|16|8 cast5-cfb|16|8 cast5-ecb|16|0 cast5-ofb|16|8 des|8|8 des-cbc|8|8 des-cfb|8|8 des-cfb1|8|8 des-cfb8|8|8 des-ecb|8|0 des-ede|16|0 des-ede-cbc|16|8 des-ede-cfb|16|8 des-ede-ofb|16|8 des-ede3|24|0 des-ede3-cbc|24|8 des-ede3-cfb|24|8 des-ede3-cfb1|24|8 des-ede3-cfb8|24|8 des-ede3-ofb|24|8 des-ofb|8|8 des3|24|8 desx|24|8 desx-cbc|24|8 idea|16|8 idea-cbc|16|8 idea-cfb|16|8 idea-ecb|16|0 idea-ofb|16|8 rc2|16|8 rc2-40-cbc|5|8 rc2-64-cbc|8|8 rc2-cbc|16|8 rc2-cfb|16|8 rc2-ecb|16|0 rc2-ofb|16|8 rc4|16|0 rc4-40|5|0 rc4-hmac-md5|16|0 seed|16|16 seed-cbc|16|16 seed-cfb|16|16 seed-ecb|16|0 seed-ofb|16|16 **NOTE: Some ciphers may not be supported by Ruby. Additionally, Ruby compiled with OpenSSL >= v1.0.1 will include AEAD ciphers, ie., aes-256-gcm.** #### Notes on patches/pull requests * Fork the project. * Make your feature addition or bug fix. * Add tests for it: this is important so I don't break it in a future version unintentionally. * Commit, do not mess with Rakefile, version, or history: if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull). * Send me a pull request: bonus points for topic branches.