symmetric-encryption 2.2.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/README.md +39 -1
 - data/Rakefile +4 -16
 - data/lib/symmetric_encryption/cipher.rb +189 -103
 - data/lib/symmetric_encryption/reader.rb +16 -5
 - data/lib/symmetric_encryption/symmetric_encryption.rb +203 -116
 - data/lib/symmetric_encryption/version.rb +1 -1
 - data/lib/symmetric_encryption/writer.rb +1 -1
 - data/test/attr_encrypted_test.rb +2 -2
 - data/test/cipher_test.rb +30 -10
 - data/test/config/symmetric-encryption.yml +41 -11
 - data/test/field_encrypted_test.rb +2 -2
 - data/test/reader_test.rb +23 -14
 - data/test/symmetric_encryption_test.rb +58 -26
 - data/test/test_db.sqlite3 +0 -0
 - data/test/writer_test.rb +4 -0
 - metadata +24 -16
 - data/Gemfile +0 -19
 - data/Gemfile.lock +0 -61
 - data/nbproject/private/config.properties +0 -0
 - data/nbproject/private/private.properties +0 -1
 - data/nbproject/private/private.xml +0 -4
 - data/nbproject/private/rake-d.txt +0 -4
 - data/nbproject/project.properties +0 -9
 - data/nbproject/project.xml +0 -16
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 8ef6c56bc1e2a9a0231a0f98f37b4cd4ca485842
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 52318d2b70e96ecfc6067e35d8c7b3ca0a339933
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 461b4904ad5b16293b4edb7f760486bbaf30a548a04debd3418c04a829d87e03f40db7a665e318e43435d140ed4fcbc07d45953158a23ac9e26c3dd725f894af
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 100babba3863c0a0b4abcdb800e6b6afe6d56ee66e43c85ff9b5cdb957f931d7660ac462e73014c07b4754df927fb264f0b5d2c77b71da0e2ba88f623c6b68b3
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -15,6 +15,30 @@ and consistent way 
     | 
|
| 
       15 
15 
     | 
    
         
             
            Symmetric Encryption uses OpenSSL to encrypt and decrypt data, and can therefore
         
     | 
| 
       16 
16 
     | 
    
         
             
            expose all the encryption algorithms supported by OpenSSL.
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
            ## Upgrading from earlier versions to SymmetricEncryption V3
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            In version 3 of SymmetricEncryption, the following changes have been made that
         
     | 
| 
      
 21 
     | 
    
         
            +
            may have backward compatibility issues:
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            * SymmetricEncryption.decrypt no longer rotates through all the decryption keys
         
     | 
| 
      
 24 
     | 
    
         
            +
              when previous ciphers fail to decrypt the encrypted string.
         
     | 
| 
      
 25 
     | 
    
         
            +
              In a very small, yet significant number of cases it was possible to decrypt data
         
     | 
| 
      
 26 
     | 
    
         
            +
              using the incorrect key. Clearly the data returned was garbage, but it still
         
     | 
| 
      
 27 
     | 
    
         
            +
              returned a string of data instead of throwing an exception.
         
     | 
| 
      
 28 
     | 
    
         
            +
              See SymmetricEncryption.select_cipher to supply your own custom logic to
         
     | 
| 
      
 29 
     | 
    
         
            +
              determine the correct cipher to use when the encrypted string does not have a
         
     | 
| 
      
 30 
     | 
    
         
            +
              header and multiple ciphers are defined.
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            * Configuration file format prior to V1 is no longer supported
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            * New configuration option has been added to support setting encryption keys
         
     | 
| 
      
 35 
     | 
    
         
            +
              from environment variables
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            * Cipher.parse_magic_header! now returns a Struct instead of an Array
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            * New config options :encrypted_key and :encrypted_iv to support setting
         
     | 
| 
      
 40 
     | 
    
         
            +
              the encryption key in environment variables
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
       18 
42 
     | 
    
         
             
            ## Security
         
     | 
| 
       19 
43 
     | 
    
         | 
| 
       20 
44 
     | 
    
         
             
            Many solutions that encrypt data require the encryption keys to be stored in the
         
     | 
| 
         @@ -50,6 +74,12 @@ From a security perspective it is important then to properly secure the system s 
     | 
|
| 
       50 
74 
     | 
    
         
             
            no hacker can switch to and run as the rails user and thereby gain access to the
         
     | 
| 
       51 
75 
     | 
    
         
             
            encryption and decryption capabilities
         
     | 
| 
       52 
76 
     | 
    
         | 
| 
      
 77 
     | 
    
         
            +
            It is not necessary to encrypt the IV (initialization vector), and it can be placed
         
     | 
| 
      
 78 
     | 
    
         
            +
            directly in the configuration file. The encryption key must be kept secure and
         
     | 
| 
      
 79 
     | 
    
         
            +
            must never be placed in the configuration file or other Rails source file in production.
         
     | 
| 
      
 80 
     | 
    
         
            +
            The IV should be generated using the rails generator described below to ensure
         
     | 
| 
      
 81 
     | 
    
         
            +
            it is a truly random key from the key space.
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
       53 
83 
     | 
    
         
             
            ## Limitations
         
     | 
| 
       54 
84 
     | 
    
         | 
| 
       55 
85 
     | 
    
         
             
            By default symmetric encryption uses the same initialization vector (IV) and
         
     | 
| 
         @@ -78,7 +108,7 @@ option will result in different encrypted output every time it is encrypted. 
     | 
|
| 
       78 
108 
     | 
    
         | 
| 
       79 
109 
     | 
    
         
             
            * Encryption of passwords in configuration files
         
     | 
| 
       80 
110 
     | 
    
         
             
            * Encryption of ActiveRecord model attributes by prefixing attributes / column
         
     | 
| 
       81 
     | 
    
         
            -
            names with encrypted_
         
     | 
| 
      
 111 
     | 
    
         
            +
              names with encrypted_
         
     | 
| 
       82 
112 
     | 
    
         
             
            * Encryption of Mongoid model fields by adding :encrypted option to field
         
     | 
| 
       83 
113 
     | 
    
         
             
              definitions
         
     | 
| 
       84 
114 
     | 
    
         
             
            * Externalization of symmetric encryption keys so that they are not in the
         
     | 
| 
         @@ -98,6 +128,14 @@ names with encrypted_ 
     | 
|
| 
       98 
128 
     | 
    
         
             
            * For maximum security supports fully random keys and initialization vectors
         
     | 
| 
       99 
129 
     | 
    
         
             
              extracted from the entire encryption key space
         
     | 
| 
       100 
130 
     | 
    
         | 
| 
      
 131 
     | 
    
         
            +
            ## Recommendations
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
            * Add the encryption header to all encrypted strings.
         
     | 
| 
      
 134 
     | 
    
         
            +
              See the _always_add_header_ option in the configuration file.
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
            * Set :random_iv => true for all ActiveRecord attributes and Mongoid fields
         
     | 
| 
      
 137 
     | 
    
         
            +
              which are not used in indexes and will not be used as part of a query.
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
       101 
139 
     | 
    
         
             
            ## Examples
         
     | 
| 
       102 
140 
     | 
    
         | 
| 
       103 
141 
     | 
    
         
             
            ### Encryption Example
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -5,26 +5,11 @@ require 'rubygems' 
     | 
|
| 
       5 
5 
     | 
    
         
             
            require 'rubygems/package'
         
     | 
| 
       6 
6 
     | 
    
         
             
            require 'rake/clean'
         
     | 
| 
       7 
7 
     | 
    
         
             
            require 'rake/testtask'
         
     | 
| 
       8 
     | 
    
         
            -
            require 'date'
         
     | 
| 
       9 
8 
     | 
    
         
             
            require 'symmetric_encryption/version'
         
     | 
| 
       10 
9 
     | 
    
         | 
| 
       11 
10 
     | 
    
         
             
            desc "Build gem"
         
     | 
| 
       12 
11 
     | 
    
         
             
            task :gem  do |t|
         
     | 
| 
       13 
     | 
    
         
            -
               
     | 
| 
       14 
     | 
    
         
            -
                s.name        = 'symmetric-encryption'
         
     | 
| 
       15 
     | 
    
         
            -
                s.version     = SymmetricEncryption::VERSION
         
     | 
| 
       16 
     | 
    
         
            -
                s.platform    = Gem::Platform::RUBY
         
     | 
| 
       17 
     | 
    
         
            -
                s.authors     = ['Reid Morrison']
         
     | 
| 
       18 
     | 
    
         
            -
                s.email       = ['reidmo@gmail.com']
         
     | 
| 
       19 
     | 
    
         
            -
                s.homepage    = 'https://github.com/ClarityServices/symmetric-encryption'
         
     | 
| 
       20 
     | 
    
         
            -
                s.date        = Date.today.to_s
         
     | 
| 
       21 
     | 
    
         
            -
                s.summary     = "Symmetric Encryption for Ruby, and Ruby on Rails"
         
     | 
| 
       22 
     | 
    
         
            -
                s.description = "SymmetricEncryption supports encrypting ActiveRecord data, Mongoid data, passwords in configuration files, encrypting and decrypting of large files through streaming"
         
     | 
| 
       23 
     | 
    
         
            -
                s.files       = FileList["./**/*"].exclude(/.gem$/, /.log$/,/^nbproject/).map{|f| f.sub(/^\.\//, '')}
         
     | 
| 
       24 
     | 
    
         
            -
                s.license     = "Apache License V2.0"
         
     | 
| 
       25 
     | 
    
         
            -
                s.has_rdoc    = true
         
     | 
| 
       26 
     | 
    
         
            -
              end
         
     | 
| 
       27 
     | 
    
         
            -
              Gem::Package.build gemspec
         
     | 
| 
      
 12 
     | 
    
         
            +
              Gem::Package.build(Gem::Specification.load('symmetric-encryption.gemspec'))
         
     | 
| 
       28 
13 
     | 
    
         
             
            end
         
     | 
| 
       29 
14 
     | 
    
         | 
| 
       30 
15 
     | 
    
         
             
            desc "Run Test Suite"
         
     | 
| 
         @@ -34,5 +19,8 @@ task :test do 
     | 
|
| 
       34 
19 
     | 
    
         
             
                t.verbose    = true
         
     | 
| 
       35 
20 
     | 
    
         
             
              end
         
     | 
| 
       36 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
              # For mongoid
         
     | 
| 
      
 23 
     | 
    
         
            +
              ENV['RACK_ENV'] = 'test'
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
       37 
25 
     | 
    
         
             
              Rake::Task['functional'].invoke
         
     | 
| 
       38 
26 
     | 
    
         
             
            end
         
     | 
| 
         @@ -7,8 +7,8 @@ module SymmetricEncryption 
     | 
|
| 
       7 
7 
     | 
    
         
             
              # threads at the same time without needing an instance of Cipher per thread
         
     | 
| 
       8 
8 
     | 
    
         
             
              class Cipher
         
     | 
| 
       9 
9 
     | 
    
         
             
                # Cipher to use for encryption and decryption
         
     | 
| 
       10 
     | 
    
         
            -
                attr_reader :cipher_name, :version
         
     | 
| 
       11 
     | 
    
         
            -
                attr_accessor :encoding
         
     | 
| 
      
 10 
     | 
    
         
            +
                attr_reader :cipher_name, :version, :iv
         
     | 
| 
      
 11 
     | 
    
         
            +
                attr_accessor :encoding, :always_add_header
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                # Available encodings
         
     | 
| 
       14 
14 
     | 
    
         
             
                ENCODINGS = [:none, :base64, :base64strict, :base16]
         
     | 
| 
         @@ -16,18 +16,29 @@ module SymmetricEncryption 
     | 
|
| 
       16 
16 
     | 
    
         
             
                # Backward compatibility
         
     | 
| 
       17 
17 
     | 
    
         
             
                alias_method :cipher, :cipher_name
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
                # Defines the Header Structure returned when parsing the header
         
     | 
| 
      
 20 
     | 
    
         
            +
                HeaderStruct = Struct.new(
         
     | 
| 
      
 21 
     | 
    
         
            +
                  :compressed,          # [true|false] Whether the data is compressed, if supplied in the header
         
     | 
| 
      
 22 
     | 
    
         
            +
                  :binary,              # [true|false] Whether the data is binary, if supplied in the header
         
     | 
| 
      
 23 
     | 
    
         
            +
                  :iv,                  # [String] IV used to encrypt the data, if supplied in the header
         
     | 
| 
      
 24 
     | 
    
         
            +
                  :key,                 # [String] Key used to encrypt the data, if supplied in the header
         
     | 
| 
      
 25 
     | 
    
         
            +
                  :cipher_name,         # [String] Name of the cipher used, if supplied in the header
         
     | 
| 
      
 26 
     | 
    
         
            +
                  :version,             # [Integer] Version of the cipher used, if supplied in the header
         
     | 
| 
      
 27 
     | 
    
         
            +
                  :decryption_cipher,   # [SymmetricEncryption::Cipher] Cipher matching the header, or SymmetricEncryption.cipher(default_version)
         
     | 
| 
      
 28 
     | 
    
         
            +
                )
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       19 
30 
     | 
    
         
             
                # Generate a new Symmetric Key pair
         
     | 
| 
       20 
31 
     | 
    
         
             
                #
         
     | 
| 
       21 
32 
     | 
    
         
             
                # Returns a hash containing a new random symmetric_key pair
         
     | 
| 
       22 
33 
     | 
    
         
             
                # consisting of a :key and :iv.
         
     | 
| 
       23 
34 
     | 
    
         
             
                # The cipher_name is also included for compatibility with the Cipher initializer
         
     | 
| 
       24 
     | 
    
         
            -
                def self.random_key_pair(cipher_name = 'aes-256-cbc' 
     | 
| 
      
 35 
     | 
    
         
            +
                def self.random_key_pair(cipher_name = 'aes-256-cbc')
         
     | 
| 
       25 
36 
     | 
    
         
             
                  openssl_cipher = ::OpenSSL::Cipher.new(cipher_name)
         
     | 
| 
       26 
37 
     | 
    
         
             
                  openssl_cipher.encrypt
         
     | 
| 
       27 
38 
     | 
    
         | 
| 
       28 
39 
     | 
    
         
             
                  {
         
     | 
| 
       29 
40 
     | 
    
         
             
                    :key         => openssl_cipher.random_key,
         
     | 
| 
       30 
     | 
    
         
            -
                    :iv          =>  
     | 
| 
      
 41 
     | 
    
         
            +
                    :iv          => openssl_cipher.random_iv,
         
     | 
| 
       31 
42 
     | 
    
         
             
                    :cipher_name => cipher_name
         
     | 
| 
       32 
43 
     | 
    
         
             
                  }
         
     | 
| 
       33 
44 
     | 
    
         
             
                end
         
     | 
| 
         @@ -65,18 +76,34 @@ module SymmetricEncryption 
     | 
|
| 
       65 
76 
     | 
    
         
             
                #     Optional. The version number of this encryption key
         
     | 
| 
       66 
77 
     | 
    
         
             
                #     Used by SymmetricEncryption to select the correct key when decrypting data
         
     | 
| 
       67 
78 
     | 
    
         
             
                #     Maximum value: 255
         
     | 
| 
       68 
     | 
    
         
            -
                 
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
      
 79 
     | 
    
         
            +
                #
         
     | 
| 
      
 80 
     | 
    
         
            +
                #   :always_add_header [true|false]
         
     | 
| 
      
 81 
     | 
    
         
            +
                #     Whether to always include the header when encrypting data.
         
     | 
| 
      
 82 
     | 
    
         
            +
                #     ** Highly recommended to set this value to true **
         
     | 
| 
      
 83 
     | 
    
         
            +
                #     Increases the length of the encrypted data by a few bytes, but makes
         
     | 
| 
      
 84 
     | 
    
         
            +
                #     migration to a new key trivial
         
     | 
| 
      
 85 
     | 
    
         
            +
                #     Default: false
         
     | 
| 
      
 86 
     | 
    
         
            +
                #     Recommended: true
         
     | 
| 
      
 87 
     | 
    
         
            +
                #
         
     | 
| 
      
 88 
     | 
    
         
            +
                def initialize(params={})
         
     | 
| 
      
 89 
     | 
    
         
            +
                  parms              = params.dup
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @key               = parms.delete(:key)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  @iv                = parms.delete(:iv)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @cipher_name       = parms.delete(:cipher_name) || parms.delete(:cipher) || 'aes-256-cbc'
         
     | 
| 
      
 93 
     | 
    
         
            +
                  @version           = parms.delete(:version)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  @always_add_header = parms.delete(:always_add_header) || false
         
     | 
| 
      
 95 
     | 
    
         
            +
                  @encoding          = (parms.delete(:encoding) || :base64).to_sym
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  raise "Missing mandatory parameter :key" unless @key
         
     | 
| 
      
 98 
     | 
    
         
            +
                  raise "Invalid Encoding: #{@encoding}" unless ENCODINGS.include?(@encoding)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  raise "Cipher version has a valid rage of 0 to 255. #{@version} is too high, or negative" if (@version.to_i > 255) || (@version.to_i < 0)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  parms.each_pair {|k,v| warn "SymmetricEncryption::Cipher Ignoring unknown option #{k.inspect} = #{v.inspect}"}
         
     | 
| 
       77 
101 
     | 
    
         
             
                end
         
     | 
| 
       78 
102 
     | 
    
         | 
| 
       79 
     | 
    
         
            -
                #  
     | 
| 
      
 103 
     | 
    
         
            +
                # Encrypt and then encode a binary or UTF-8 string
         
     | 
| 
      
 104 
     | 
    
         
            +
                #
         
     | 
| 
      
 105 
     | 
    
         
            +
                # Returns data encrypted and then encoded according to the encoding setting
         
     | 
| 
      
 106 
     | 
    
         
            +
                #         of this cipher
         
     | 
| 
       80 
107 
     | 
    
         
             
                # Returns nil if str is nil
         
     | 
| 
       81 
108 
     | 
    
         
             
                # Returns "" str is empty
         
     | 
| 
       82 
109 
     | 
    
         
             
                #
         
     | 
| 
         @@ -105,7 +132,7 @@ module SymmetricEncryption 
     | 
|
| 
       105 
132 
     | 
    
         
             
                #   compress [true|false]
         
     | 
| 
       106 
133 
     | 
    
         
             
                #     Whether to compress str before encryption
         
     | 
| 
       107 
134 
     | 
    
         
             
                #     Should only be used for large strings since compression overhead and
         
     | 
| 
       108 
     | 
    
         
            -
                #     the overhead of adding the  
     | 
| 
      
 135 
     | 
    
         
            +
                #     the overhead of adding the encryption header may exceed any benefits of
         
     | 
| 
       109 
136 
     | 
    
         
             
                #     compression
         
     | 
| 
       110 
137 
     | 
    
         
             
                #     Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
         
     | 
| 
       111 
138 
     | 
    
         
             
                #     Default: false
         
     | 
| 
         @@ -117,40 +144,36 @@ module SymmetricEncryption 
     | 
|
| 
       117 
144 
     | 
    
         
             
                  self.encode(encrypted)
         
     | 
| 
       118 
145 
     | 
    
         
             
                end
         
     | 
| 
       119 
146 
     | 
    
         | 
| 
       120 
     | 
    
         
            -
                #  
     | 
| 
      
 147 
     | 
    
         
            +
                # Decode and Decrypt string
         
     | 
| 
      
 148 
     | 
    
         
            +
                #   Returns a decrypted string after decoding it first according to the
         
     | 
| 
      
 149 
     | 
    
         
            +
                #           encoding setting of this cipher
         
     | 
| 
      
 150 
     | 
    
         
            +
                #   Returns nil if encrypted_string is nil
         
     | 
| 
      
 151 
     | 
    
         
            +
                #   Returns '' if encrypted_string == ''
         
     | 
| 
       121 
152 
     | 
    
         
             
                #
         
     | 
| 
       122 
     | 
    
         
            -
                #  
     | 
| 
      
 153 
     | 
    
         
            +
                # Parameters
         
     | 
| 
      
 154 
     | 
    
         
            +
                #   encrypted_string [String]
         
     | 
| 
      
 155 
     | 
    
         
            +
                #     Binary encrypted string to decrypt
         
     | 
| 
       123 
156 
     | 
    
         
             
                #
         
     | 
| 
       124 
     | 
    
         
            -
                # 
     | 
| 
       125 
     | 
    
         
            -
                # 
     | 
| 
       126 
     | 
    
         
            -
                # 
     | 
| 
       127 
     | 
    
         
            -
                 
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
                 
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
                    crypt(:decrypt, decoded)
         
     | 
| 
       142 
     | 
    
         
            -
                  end
         
     | 
| 
       143 
     | 
    
         
            -
                end
         
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
                # Return a new random key using the configured cipher_name
         
     | 
| 
       146 
     | 
    
         
            -
                # Useful for generating new symmetric keys
         
     | 
| 
       147 
     | 
    
         
            -
                def random_key
         
     | 
| 
       148 
     | 
    
         
            -
                  ::OpenSSL::Cipher::Cipher.new(@cipher_name).random_key
         
     | 
| 
       149 
     | 
    
         
            -
                end
         
     | 
| 
      
 157 
     | 
    
         
            +
                #   header [HeaderStruct]
         
     | 
| 
      
 158 
     | 
    
         
            +
                #     Optional header for the supplied encrypted_string
         
     | 
| 
      
 159 
     | 
    
         
            +
                #
         
     | 
| 
      
 160 
     | 
    
         
            +
                #   binary [true|false]
         
     | 
| 
      
 161 
     | 
    
         
            +
                #     If no header is supplied then determines whether the string returned
         
     | 
| 
      
 162 
     | 
    
         
            +
                #     is binary or UTF8
         
     | 
| 
      
 163 
     | 
    
         
            +
                #
         
     | 
| 
      
 164 
     | 
    
         
            +
                # Reads the 'magic' header if present for key, iv, cipher_name and compression
         
     | 
| 
      
 165 
     | 
    
         
            +
                #
         
     | 
| 
      
 166 
     | 
    
         
            +
                # encrypted_string must be in raw binary form when calling this method
         
     | 
| 
      
 167 
     | 
    
         
            +
                #
         
     | 
| 
      
 168 
     | 
    
         
            +
                # Creates a new OpenSSL::Cipher with every call so that this call
         
     | 
| 
      
 169 
     | 
    
         
            +
                # is thread-safe and can be called concurrently by multiple threads with
         
     | 
| 
      
 170 
     | 
    
         
            +
                # the same instance of Cipher
         
     | 
| 
      
 171 
     | 
    
         
            +
                def decrypt(str)
         
     | 
| 
      
 172 
     | 
    
         
            +
                  decoded = self.decode(str)
         
     | 
| 
      
 173 
     | 
    
         
            +
                  return unless decoded
         
     | 
| 
       150 
174 
     | 
    
         | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                  ::OpenSSL::Cipher::Cipher.new(@cipher_name).block_size
         
     | 
| 
      
 175 
     | 
    
         
            +
                  return decoded if decoded.empty?
         
     | 
| 
      
 176 
     | 
    
         
            +
                  binary_decrypt(decoded).force_encoding(SymmetricEncryption::UTF8_ENCODING)
         
     | 
| 
       154 
177 
     | 
    
         
             
                end
         
     | 
| 
       155 
178 
     | 
    
         | 
| 
       156 
179 
     | 
    
         
             
                # Returns UTF8 encoded string after encoding the supplied Binary string
         
     | 
| 
         @@ -161,16 +184,22 @@ module SymmetricEncryption 
     | 
|
| 
       161 
184 
     | 
    
         
             
                #
         
     | 
| 
       162 
185 
     | 
    
         
             
                # Returned string is UTF8 encoded except for encoding :none
         
     | 
| 
       163 
186 
     | 
    
         
             
                def encode(binary_string)
         
     | 
| 
       164 
     | 
    
         
            -
                  return  
     | 
| 
      
 187 
     | 
    
         
            +
                  return binary_string if binary_string.nil? || (binary_string == '')
         
     | 
| 
       165 
188 
     | 
    
         | 
| 
       166 
189 
     | 
    
         
             
                  # Now encode data based on encoding setting
         
     | 
| 
       167 
190 
     | 
    
         
             
                  case encoding
         
     | 
| 
       168 
191 
     | 
    
         
             
                  when :base64
         
     | 
| 
       169 
     | 
    
         
            -
                    ::Base64.encode64(binary_string) 
     | 
| 
      
 192 
     | 
    
         
            +
                    encoded_string = ::Base64.encode64(binary_string)
         
     | 
| 
      
 193 
     | 
    
         
            +
                    # Support Ruby 1.9 encoding
         
     | 
| 
      
 194 
     | 
    
         
            +
                    defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
         
     | 
| 
       170 
195 
     | 
    
         
             
                  when :base64strict
         
     | 
| 
       171 
     | 
    
         
            -
                    ::Base64.encode64(binary_string).gsub(/\n/, '') 
     | 
| 
      
 196 
     | 
    
         
            +
                    encoded_string = ::Base64.encode64(binary_string).gsub(/\n/, '')
         
     | 
| 
      
 197 
     | 
    
         
            +
                    # Support Ruby 1.9 encoding
         
     | 
| 
      
 198 
     | 
    
         
            +
                    defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
         
     | 
| 
       172 
199 
     | 
    
         
             
                  when :base16
         
     | 
| 
       173 
     | 
    
         
            -
                    binary_string.to_s.unpack('H*').first 
     | 
| 
      
 200 
     | 
    
         
            +
                    encoded_string = binary_string.to_s.unpack('H*').first
         
     | 
| 
      
 201 
     | 
    
         
            +
                    # Support Ruby 1.9 encoding
         
     | 
| 
      
 202 
     | 
    
         
            +
                    defined?(Encoding) ? encoded_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) : encoded_string
         
     | 
| 
       174 
203 
     | 
    
         
             
                  else
         
     | 
| 
       175 
204 
     | 
    
         
             
                    binary_string
         
     | 
| 
       176 
205 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -181,42 +210,53 @@ module SymmetricEncryption 
     | 
|
| 
       181 
210 
     | 
    
         
             
                #
         
     | 
| 
       182 
211 
     | 
    
         
             
                # Returned string is Binary encoded
         
     | 
| 
       183 
212 
     | 
    
         
             
                def decode(encoded_string)
         
     | 
| 
       184 
     | 
    
         
            -
                  return  
     | 
| 
      
 213 
     | 
    
         
            +
                  return encoded_string if encoded_string.nil? || (encoded_string == '')
         
     | 
| 
       185 
214 
     | 
    
         | 
| 
       186 
215 
     | 
    
         
             
                  case encoding
         
     | 
| 
       187 
216 
     | 
    
         
             
                  when :base64, :base64strict
         
     | 
| 
       188 
     | 
    
         
            -
                    ::Base64.decode64(encoded_string) 
     | 
| 
      
 217 
     | 
    
         
            +
                    decoded_string = ::Base64.decode64(encoded_string)
         
     | 
| 
      
 218 
     | 
    
         
            +
                    # Support Ruby 1.9 encoding
         
     | 
| 
      
 219 
     | 
    
         
            +
                    defined?(Encoding) ? decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING) : decoded_string
         
     | 
| 
       189 
220 
     | 
    
         
             
                  when :base16
         
     | 
| 
       190 
     | 
    
         
            -
                    [encoded_string].pack('H*') 
     | 
| 
      
 221 
     | 
    
         
            +
                    decoded_string = [encoded_string].pack('H*')
         
     | 
| 
      
 222 
     | 
    
         
            +
                    # Support Ruby 1.9 encoding
         
     | 
| 
      
 223 
     | 
    
         
            +
                    defined?(Encoding) ? decoded_string.force_encoding(SymmetricEncryption::BINARY_ENCODING) : decoded_string
         
     | 
| 
       191 
224 
     | 
    
         
             
                  else
         
     | 
| 
       192 
225 
     | 
    
         
             
                    encoded_string
         
     | 
| 
       193 
226 
     | 
    
         
             
                  end
         
     | 
| 
       194 
227 
     | 
    
         
             
                end
         
     | 
| 
       195 
228 
     | 
    
         | 
| 
       196 
     | 
    
         
            -
                #  
     | 
| 
       197 
     | 
    
         
            -
                #  
     | 
| 
       198 
     | 
    
         
            -
                 
     | 
| 
       199 
     | 
    
         
            -
             
     | 
| 
       200 
     | 
    
         
            -
                 
     | 
| 
       201 
     | 
    
         
            -
             
     | 
| 
       202 
     | 
    
         
            -
                # 
     | 
| 
      
 229 
     | 
    
         
            +
                # Return a new random key using the configured cipher_name
         
     | 
| 
      
 230 
     | 
    
         
            +
                # Useful for generating new symmetric keys
         
     | 
| 
      
 231 
     | 
    
         
            +
                def random_key
         
     | 
| 
      
 232 
     | 
    
         
            +
                  ::OpenSSL::Cipher::Cipher.new(@cipher_name).random_key
         
     | 
| 
      
 233 
     | 
    
         
            +
                end
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
                # Returns the block size for the configured cipher_name
         
     | 
| 
      
 236 
     | 
    
         
            +
                def block_size
         
     | 
| 
      
 237 
     | 
    
         
            +
                  ::OpenSSL::Cipher::Cipher.new(@cipher_name).block_size
         
     | 
| 
      
 238 
     | 
    
         
            +
                end
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                # Returns whether the supplied buffer starts with a symmetric_encryption header
         
     | 
| 
      
 241 
     | 
    
         
            +
                # Note: The encoding of the supplied buffer is forced to binary if not already binary
         
     | 
| 
      
 242 
     | 
    
         
            +
                def self.has_header?(buffer)
         
     | 
| 
      
 243 
     | 
    
         
            +
                  return false if buffer.nil? || (buffer == '')
         
     | 
| 
      
 244 
     | 
    
         
            +
                  buffer.force_encoding(SymmetricEncryption::BINARY_ENCODING) if buffer.respond_to?(:force_encoding)
         
     | 
| 
      
 245 
     | 
    
         
            +
                  buffer.start_with?(MAGIC_HEADER)
         
     | 
| 
      
 246 
     | 
    
         
            +
                end
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
                # Returns HeaderStruct of the header parsed from the supplied string
         
     | 
| 
      
 249 
     | 
    
         
            +
                # Returns nil if no header is present
         
     | 
| 
       203 
250 
     | 
    
         
             
                #
         
     | 
| 
       204 
     | 
    
         
            -
                # The supplied buffer will be updated directly and  
     | 
| 
       205 
     | 
    
         
            -
                #  
     | 
| 
      
 251 
     | 
    
         
            +
                # The supplied buffer will be updated directly and its header will be
         
     | 
| 
      
 252 
     | 
    
         
            +
                # stripped if present
         
     | 
| 
       206 
253 
     | 
    
         
             
                #
         
     | 
| 
       207 
254 
     | 
    
         
             
                # Parameters
         
     | 
| 
       208 
255 
     | 
    
         
             
                #   buffer
         
     | 
| 
       209 
     | 
    
         
            -
                #     String to extract the header from 
     | 
| 
       210 
     | 
    
         
            -
                #
         
     | 
| 
       211 
     | 
    
         
            -
                #   default_version
         
     | 
| 
       212 
     | 
    
         
            -
                #     If no header is present, this is the default value for the version
         
     | 
| 
       213 
     | 
    
         
            -
                #     of the cipher to use
         
     | 
| 
      
 256 
     | 
    
         
            +
                #     String to extract the header from
         
     | 
| 
       214 
257 
     | 
    
         
             
                #
         
     | 
| 
       215 
     | 
    
         
            -
                 
     | 
| 
       216 
     | 
    
         
            -
             
     | 
| 
       217 
     | 
    
         
            -
                def self.parse_magic_header!(buffer, default_version=nil, default_compressed=false)
         
     | 
| 
       218 
     | 
    
         
            -
                  buffer.force_encoding(SymmetricEncryption::BINARY_ENCODING) if buffer
         
     | 
| 
       219 
     | 
    
         
            -
                  return [default_compressed, nil, nil, nil, nil, SymmetricEncryption.cipher(default_version)] unless buffer && buffer.start_with?(MAGIC_HEADER)
         
     | 
| 
      
 258 
     | 
    
         
            +
                def self.parse_header!(buffer)
         
     | 
| 
      
 259 
     | 
    
         
            +
                  return unless has_header?(buffer)
         
     | 
| 
       220 
260 
     | 
    
         | 
| 
       221 
261 
     | 
    
         
             
                  # Header includes magic header and version byte
         
     | 
| 
       222 
262 
     | 
    
         
             
                  # Remove header and extract flags
         
     | 
| 
         @@ -225,6 +265,7 @@ module SymmetricEncryption 
     | 
|
| 
       225 
265 
     | 
    
         
             
                  include_iv    = (flags & 0b0100_0000_0000_0000) != 0
         
     | 
| 
       226 
266 
     | 
    
         
             
                  include_key   = (flags & 0b0010_0000_0000_0000) != 0
         
     | 
| 
       227 
267 
     | 
    
         
             
                  include_cipher= (flags & 0b0001_0000_0000_0000) != 0
         
     | 
| 
      
 268 
     | 
    
         
            +
                  binary        = (flags & 0b0000_1000_0000_0000) != 0
         
     | 
| 
       228 
269 
     | 
    
         
             
                  # Version of the key to use to decrypt the key if present,
         
     | 
| 
       229 
270 
     | 
    
         
             
                  # otherwise to decrypt the data following the header
         
     | 
| 
       230 
271 
     | 
    
         
             
                  version       = flags & 0b0000_0000_1111_1111
         
     | 
| 
         @@ -238,14 +279,14 @@ module SymmetricEncryption 
     | 
|
| 
       238 
279 
     | 
    
         
             
                  end
         
     | 
| 
       239 
280 
     | 
    
         
             
                  if include_key
         
     | 
| 
       240 
281 
     | 
    
         
             
                    len = buffer.slice!(0..1).unpack('v').first
         
     | 
| 
       241 
     | 
    
         
            -
                    key = decryption_cipher.binary_decrypt(buffer.slice!(0..len-1))
         
     | 
| 
      
 282 
     | 
    
         
            +
                    key = decryption_cipher.binary_decrypt(buffer.slice!(0..len-1), header=false, binary=true)
         
     | 
| 
       242 
283 
     | 
    
         
             
                  end
         
     | 
| 
       243 
284 
     | 
    
         
             
                  if include_cipher
         
     | 
| 
       244 
285 
     | 
    
         
             
                    len    = buffer.slice!(0..1).unpack('v').first
         
     | 
| 
       245 
286 
     | 
    
         
             
                    cipher_name = buffer.slice!(0..len-1)
         
     | 
| 
       246 
287 
     | 
    
         
             
                  end
         
     | 
| 
       247 
288 
     | 
    
         | 
| 
       248 
     | 
    
         
            -
                   
     | 
| 
      
 289 
     | 
    
         
            +
                  HeaderStruct.new(compressed, binary, iv, key, cipher_name, version, decryption_cipher)
         
     | 
| 
       249 
290 
     | 
    
         
             
                end
         
     | 
| 
       250 
291 
     | 
    
         | 
| 
       251 
292 
     | 
    
         
             
                # Returns a magic header for this cipher instance that can be placed at
         
     | 
| 
         @@ -269,16 +310,15 @@ module SymmetricEncryption 
     | 
|
| 
       269 
310 
     | 
    
         
             
                #     Includes the cipher_name used. For example 'aes-256-cbc'
         
     | 
| 
       270 
311 
     | 
    
         
             
                #     The cipher_name string to to put in the header
         
     | 
| 
       271 
312 
     | 
    
         
             
                #     Default: nil : Exclude cipher_name name from header
         
     | 
| 
       272 
     | 
    
         
            -
                 
     | 
| 
      
 313 
     | 
    
         
            +
                #
         
     | 
| 
      
 314 
     | 
    
         
            +
                #   binary
         
     | 
| 
      
 315 
     | 
    
         
            +
                #     Whether the data being encrypted is binary.
         
     | 
| 
      
 316 
     | 
    
         
            +
                #     When the header is read, it sets the encoding of the string returned to Binary
         
     | 
| 
      
 317 
     | 
    
         
            +
                def self.build_header(version, compressed=false, iv=nil, key=nil, cipher_name=nil, binary=false)
         
     | 
| 
       273 
318 
     | 
    
         
             
                  # Ruby V2 named parameters would be perfect here
         
     | 
| 
       274 
319 
     | 
    
         | 
| 
       275 
     | 
    
         
            -
                  #  
     | 
| 
       276 
     | 
    
         
            -
                  flags = version || 0 # Same as 0b0000_0000_0000_0000
         
     | 
| 
       277 
     | 
    
         
            -
             
     | 
| 
       278 
     | 
    
         
            -
                  # Replace version with global cipher that will be used to encrypt the random key
         
     | 
| 
       279 
     | 
    
         
            -
                  if iv || key
         
     | 
| 
       280 
     | 
    
         
            -
                    flags = (SymmetricEncryption.cipher.version || 0)
         
     | 
| 
       281 
     | 
    
         
            -
                  end
         
     | 
| 
      
 320 
     | 
    
         
            +
                  # Version number of supplied encryption key, or use the global cipher version if none was supplied
         
     | 
| 
      
 321 
     | 
    
         
            +
                  flags = iv || key ? (SymmetricEncryption.cipher.version || 0) : (version || 0) # Same as 0b0000_0000_0000_0000
         
     | 
| 
       282 
322 
     | 
    
         | 
| 
       283 
323 
     | 
    
         
             
                  # If the data is to be compressed before being encrypted, set the
         
     | 
| 
       284 
324 
     | 
    
         
             
                  # compressed bit in the flags word
         
     | 
| 
         @@ -286,13 +326,14 @@ module SymmetricEncryption 
     | 
|
| 
       286 
326 
     | 
    
         
             
                  flags |= 0b0100_0000_0000_0000 if iv
         
     | 
| 
       287 
327 
     | 
    
         
             
                  flags |= 0b0010_0000_0000_0000 if key
         
     | 
| 
       288 
328 
     | 
    
         
             
                  flags |= 0b0001_0000_0000_0000 if cipher_name
         
     | 
| 
      
 329 
     | 
    
         
            +
                  flags |= 0b0000_1000_0000_0000 if binary
         
     | 
| 
       289 
330 
     | 
    
         
             
                  header = "#{MAGIC_HEADER}#{[flags].pack('v')}".force_encoding(SymmetricEncryption::BINARY_ENCODING)
         
     | 
| 
       290 
331 
     | 
    
         
             
                  if iv
         
     | 
| 
       291 
332 
     | 
    
         
             
                    header << [iv.length].pack('v')
         
     | 
| 
       292 
333 
     | 
    
         
             
                    header << iv
         
     | 
| 
       293 
334 
     | 
    
         
             
                  end
         
     | 
| 
       294 
335 
     | 
    
         
             
                  if key
         
     | 
| 
       295 
     | 
    
         
            -
                    encrypted = SymmetricEncryption.cipher.binary_encrypt(key, false, false)
         
     | 
| 
      
 336 
     | 
    
         
            +
                    encrypted = SymmetricEncryption.cipher.binary_encrypt(key, false, false, false)
         
     | 
| 
       296 
337 
     | 
    
         
             
                    header << [encrypted.length].pack('v').force_encoding(SymmetricEncryption::BINARY_ENCODING)
         
     | 
| 
       297 
338 
     | 
    
         
             
                    header << encrypted
         
     | 
| 
       298 
339 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -307,21 +348,33 @@ module SymmetricEncryption 
     | 
|
| 
       307 
348 
     | 
    
         
             
                #
         
     | 
| 
       308 
349 
     | 
    
         
             
                # Returns a Binary encrypted string without applying any Base64, or other encoding
         
     | 
| 
       309 
350 
     | 
    
         
             
                #
         
     | 
| 
       310 
     | 
    
         
            -
                #    
     | 
| 
      
 351 
     | 
    
         
            +
                #   add_header [nil|true|false]
         
     | 
| 
      
 352 
     | 
    
         
            +
                #     Whether to add a header to the encrypted string
         
     | 
| 
      
 353 
     | 
    
         
            +
                #     If not supplied it defaults to true if always_add_header || random_iv || compress
         
     | 
| 
      
 354 
     | 
    
         
            +
                #     Default: nil
         
     | 
| 
       311 
355 
     | 
    
         
             
                #
         
     | 
| 
       312 
356 
     | 
    
         
             
                # Creates a new OpenSSL::Cipher with every call so that this call
         
     | 
| 
       313 
357 
     | 
    
         
             
                # is thread-safe
         
     | 
| 
       314 
358 
     | 
    
         
             
                #
         
     | 
| 
       315 
359 
     | 
    
         
             
                # See #encrypt to encrypt and encode the result as a string
         
     | 
| 
       316 
     | 
    
         
            -
                def binary_encrypt( 
     | 
| 
      
 360 
     | 
    
         
            +
                def binary_encrypt(str, random_iv=false, compress=false, add_header=nil)
         
     | 
| 
      
 361 
     | 
    
         
            +
                  return if str.nil?
         
     | 
| 
      
 362 
     | 
    
         
            +
                  string = str.to_s
         
     | 
| 
      
 363 
     | 
    
         
            +
                  return string if string.empty?
         
     | 
| 
      
 364 
     | 
    
         
            +
             
     | 
| 
      
 365 
     | 
    
         
            +
                  # Creates a new OpenSSL::Cipher with every call so that this call
         
     | 
| 
      
 366 
     | 
    
         
            +
                  # is thread-safe
         
     | 
| 
       317 
367 
     | 
    
         
             
                  openssl_cipher = ::OpenSSL::Cipher.new(self.cipher_name)
         
     | 
| 
       318 
368 
     | 
    
         
             
                  openssl_cipher.encrypt
         
     | 
| 
       319 
369 
     | 
    
         
             
                  openssl_cipher.key = @key
         
     | 
| 
       320 
     | 
    
         
            -
                   
     | 
| 
      
 370 
     | 
    
         
            +
                  add_header = always_add_header || random_iv || compress if add_header.nil?
         
     | 
| 
      
 371 
     | 
    
         
            +
                  result = if add_header
         
     | 
| 
       321 
372 
     | 
    
         
             
                    # Random iv and compress both add the magic header
         
     | 
| 
       322 
373 
     | 
    
         
             
                    iv = random_iv ? openssl_cipher.random_iv : @iv
         
     | 
| 
       323 
374 
     | 
    
         
             
                    openssl_cipher.iv = iv if iv
         
     | 
| 
       324 
     | 
    
         
            -
                     
     | 
| 
      
 375 
     | 
    
         
            +
                    # Set the binary indicator on the header if string is Binary Encoded
         
     | 
| 
      
 376 
     | 
    
         
            +
                    binary = (string.encoding == SymmetricEncryption::BINARY_ENCODING)
         
     | 
| 
      
 377 
     | 
    
         
            +
                    self.class.build_header(version, compress, random_iv ? iv : nil, binary) +
         
     | 
| 
       325 
378 
     | 
    
         
             
                      openssl_cipher.update(compress ? Zlib::Deflate.deflate(string) : string)
         
     | 
| 
       326 
379 
     | 
    
         
             
                  else
         
     | 
| 
       327 
380 
     | 
    
         
             
                    openssl_cipher.iv = @iv if @iv
         
     | 
| 
         @@ -331,48 +384,81 @@ module SymmetricEncryption 
     | 
|
| 
       331 
384 
     | 
    
         
             
                end
         
     | 
| 
       332 
385 
     | 
    
         | 
| 
       333 
386 
     | 
    
         
             
                # Advanced use only
         
     | 
| 
      
 387 
     | 
    
         
            +
                # See #decrypt to decrypt encoded strings
         
     | 
| 
       334 
388 
     | 
    
         
             
                #
         
     | 
| 
       335 
389 
     | 
    
         
             
                # Returns a Binary decrypted string without decoding the string first
         
     | 
| 
       336 
390 
     | 
    
         
             
                #
         
     | 
| 
      
 391 
     | 
    
         
            +
                # Decryption of supplied string
         
     | 
| 
      
 392 
     | 
    
         
            +
                #   Returns the decrypted string
         
     | 
| 
      
 393 
     | 
    
         
            +
                #   Returns nil if encrypted_string is nil
         
     | 
| 
      
 394 
     | 
    
         
            +
                #   Returns '' if encrypted_string == ''
         
     | 
| 
      
 395 
     | 
    
         
            +
                #
         
     | 
| 
      
 396 
     | 
    
         
            +
                # Parameters
         
     | 
| 
      
 397 
     | 
    
         
            +
                #   encrypted_string [String]
         
     | 
| 
      
 398 
     | 
    
         
            +
                #     Binary encrypted string to decrypt
         
     | 
| 
      
 399 
     | 
    
         
            +
                #
         
     | 
| 
      
 400 
     | 
    
         
            +
                #   header [HeaderStruct]
         
     | 
| 
      
 401 
     | 
    
         
            +
                #     Optional header for the supplied encrypted_string
         
     | 
| 
      
 402 
     | 
    
         
            +
                #
         
     | 
| 
      
 403 
     | 
    
         
            +
                #   binary [true|false]
         
     | 
| 
      
 404 
     | 
    
         
            +
                #     If no header is supplied then determines whether the string returned
         
     | 
| 
      
 405 
     | 
    
         
            +
                #     is binary or UTF8
         
     | 
| 
      
 406 
     | 
    
         
            +
                #
         
     | 
| 
       337 
407 
     | 
    
         
             
                # Reads the 'magic' header if present for key, iv, cipher_name and compression
         
     | 
| 
       338 
408 
     | 
    
         
             
                #
         
     | 
| 
       339 
409 
     | 
    
         
             
                # encrypted_string must be in raw binary form when calling this method
         
     | 
| 
       340 
410 
     | 
    
         
             
                #
         
     | 
| 
       341 
411 
     | 
    
         
             
                # Creates a new OpenSSL::Cipher with every call so that this call
         
     | 
| 
       342 
     | 
    
         
            -
                # is thread-safe
         
     | 
| 
       343 
     | 
    
         
            -
                #
         
     | 
| 
       344 
     | 
    
         
            -
                # 
     | 
| 
       345 
     | 
    
         
            -
                 
     | 
| 
      
 412 
     | 
    
         
            +
                # is thread-safe and can be called concurrently by multiple threads with
         
     | 
| 
      
 413 
     | 
    
         
            +
                # the same instance of Cipher
         
     | 
| 
      
 414 
     | 
    
         
            +
                #
         
     | 
| 
      
 415 
     | 
    
         
            +
                # Note:
         
     | 
| 
      
 416 
     | 
    
         
            +
                #   When a string is encrypted and the header is used, its decrypted form
         
     | 
| 
      
 417 
     | 
    
         
            +
                #   is automatically set to the same UTF-8 or Binary encoding
         
     | 
| 
      
 418 
     | 
    
         
            +
                def binary_decrypt(encrypted_string, header=nil, binary=false)
         
     | 
| 
      
 419 
     | 
    
         
            +
                  return if encrypted_string.nil?
         
     | 
| 
       346 
420 
     | 
    
         
             
                  str = encrypted_string.to_s
         
     | 
| 
       347 
     | 
    
         
            -
                  if str. 
     | 
| 
      
 421 
     | 
    
         
            +
                  str.force_encoding(SymmetricEncryption::BINARY_ENCODING) if str.respond_to?(:force_encoding)
         
     | 
| 
      
 422 
     | 
    
         
            +
                  return str if str.empty?
         
     | 
| 
      
 423 
     | 
    
         
            +
             
     | 
| 
      
 424 
     | 
    
         
            +
                  decrypted_string = if header || self.class.has_header?(str)
         
     | 
| 
       348 
425 
     | 
    
         
             
                    str = str.dup
         
     | 
| 
       349 
     | 
    
         
            -
                     
     | 
| 
       350 
     | 
    
         
            -
                     
     | 
| 
      
 426 
     | 
    
         
            +
                    header ||= self.class.parse_header!(str)
         
     | 
| 
      
 427 
     | 
    
         
            +
                    binary = header.binary
         
     | 
| 
      
 428 
     | 
    
         
            +
             
     | 
| 
      
 429 
     | 
    
         
            +
                    openssl_cipher = ::OpenSSL::Cipher.new(header.cipher_name || self.cipher_name)
         
     | 
| 
       351 
430 
     | 
    
         
             
                    openssl_cipher.decrypt
         
     | 
| 
       352 
     | 
    
         
            -
                    openssl_cipher.key = key || @key
         
     | 
| 
       353 
     | 
    
         
            -
                    iv  
     | 
| 
      
 431 
     | 
    
         
            +
                    openssl_cipher.key = header.key || @key
         
     | 
| 
      
 432 
     | 
    
         
            +
                    iv = header.iv || @iv
         
     | 
| 
       354 
433 
     | 
    
         
             
                    openssl_cipher.iv = iv if iv
         
     | 
| 
       355 
434 
     | 
    
         
             
                    result = openssl_cipher.update(str)
         
     | 
| 
       356 
435 
     | 
    
         
             
                    result << openssl_cipher.final
         
     | 
| 
       357 
     | 
    
         
            -
                    compressed ? Zlib::Inflate.inflate(result) : result
         
     | 
| 
      
 436 
     | 
    
         
            +
                    header.compressed ? Zlib::Inflate.inflate(result) : result
         
     | 
| 
       358 
437 
     | 
    
         
             
                  else
         
     | 
| 
       359 
438 
     | 
    
         
             
                    openssl_cipher = ::OpenSSL::Cipher.new(self.cipher_name)
         
     | 
| 
       360 
439 
     | 
    
         
             
                    openssl_cipher.decrypt
         
     | 
| 
       361 
440 
     | 
    
         
             
                    openssl_cipher.key = @key
         
     | 
| 
       362 
441 
     | 
    
         
             
                    openssl_cipher.iv = @iv if @iv
         
     | 
| 
       363 
     | 
    
         
            -
                    result = openssl_cipher.update( 
     | 
| 
      
 442 
     | 
    
         
            +
                    result = openssl_cipher.update(str)
         
     | 
| 
       364 
443 
     | 
    
         
             
                    result << openssl_cipher.final
         
     | 
| 
       365 
444 
     | 
    
         
             
                  end
         
     | 
| 
      
 445 
     | 
    
         
            +
             
     | 
| 
      
 446 
     | 
    
         
            +
                  # Support Ruby 1.9 and above Encoding
         
     | 
| 
      
 447 
     | 
    
         
            +
                  if defined?(Encoding)
         
     | 
| 
      
 448 
     | 
    
         
            +
                    # Sets the encoding of the result string to UTF8 or BINARY based on the binary header
         
     | 
| 
      
 449 
     | 
    
         
            +
                    binary ? decrypted_string.force_encoding(SymmetricEncryption::BINARY_ENCODING) : decrypted_string.force_encoding(SymmetricEncryption::UTF8_ENCODING)
         
     | 
| 
      
 450 
     | 
    
         
            +
                  else
         
     | 
| 
      
 451 
     | 
    
         
            +
                    decrypted_string
         
     | 
| 
      
 452 
     | 
    
         
            +
                  end
         
     | 
| 
       366 
453 
     | 
    
         
             
                end
         
     | 
| 
       367 
454 
     | 
    
         | 
| 
       368 
     | 
    
         
            -
                # Returns [String] object represented as a string
         
     | 
| 
       369 
     | 
    
         
            -
                # Excluding the key and iv
         
     | 
| 
      
 455 
     | 
    
         
            +
                # Returns [String] object represented as a string, filtering out the key
         
     | 
| 
       370 
456 
     | 
    
         
             
                def inspect
         
     | 
| 
       371 
     | 
    
         
            -
             
     | 
| 
      
 457 
     | 
    
         
            +
                  "#<#{self.class}:0x#{self.__id__.to_s(16)} @key=\"[FILTERED]\" @iv=#{iv.inspect} @cipher_name=#{cipher_name.inspect}, @version=#{version.inspect}, @encoding=#{encoding.inspect}"
         
     | 
| 
       372 
458 
     | 
    
         
             
                end
         
     | 
| 
       373 
459 
     | 
    
         | 
| 
       374 
460 
     | 
    
         
             
                private
         
     | 
| 
       375 
461 
     | 
    
         | 
| 
       376 
     | 
    
         
            -
                attr_reader :key 
     | 
| 
      
 462 
     | 
    
         
            +
                attr_reader :key
         
     | 
| 
       377 
463 
     | 
    
         
             
              end
         
     | 
| 
       378 
     | 
    
         
            -
            end
         
     | 
| 
      
 464 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -311,20 +311,31 @@ module SymmetricEncryption 
     | 
|
| 
       311 
311 
     | 
    
         | 
| 
       312 
312 
     | 
    
         
             
                # Read the header from the file if present
         
     | 
| 
       313 
313 
     | 
    
         
             
                def read_header
         
     | 
| 
       314 
     | 
    
         
            -
                  @compressed = nil
         
     | 
| 
       315 
314 
     | 
    
         
             
                  @pos = 0
         
     | 
| 
       316 
315 
     | 
    
         | 
| 
       317 
316 
     | 
    
         
             
                  # Read first block and check for the header
         
     | 
| 
       318 
317 
     | 
    
         
             
                  buf = @ios.read(@buffer_size)
         
     | 
| 
       319 
318 
     | 
    
         | 
| 
       320 
319 
     | 
    
         
             
                  # Use cipher specified in header, or global cipher if it has no header
         
     | 
| 
       321 
     | 
    
         
            -
                   
     | 
| 
       322 
     | 
    
         
            -
                   
     | 
| 
      
 320 
     | 
    
         
            +
                  iv, key, cipher_name, decryption_cipher = nil
         
     | 
| 
      
 321 
     | 
    
         
            +
                  if header = SymmetricEncryption::Cipher.parse_header!(buf)
         
     | 
| 
      
 322 
     | 
    
         
            +
                    @header_present   = true
         
     | 
| 
      
 323 
     | 
    
         
            +
                    @compressed       = header.compressed
         
     | 
| 
      
 324 
     | 
    
         
            +
                    decryption_cipher = header.decryption_cipher
         
     | 
| 
      
 325 
     | 
    
         
            +
                    cipher_name       = header.cipher_name || decryption_cipher.cipher_name
         
     | 
| 
      
 326 
     | 
    
         
            +
                    key               = header.key
         
     | 
| 
      
 327 
     | 
    
         
            +
                    iv                = header.iv
         
     | 
| 
      
 328 
     | 
    
         
            +
                  else
         
     | 
| 
      
 329 
     | 
    
         
            +
                    @header_present   = false
         
     | 
| 
      
 330 
     | 
    
         
            +
                    @compressed       = nil
         
     | 
| 
      
 331 
     | 
    
         
            +
                    decryption_cipher = SymmetricEncryption.cipher(@version)
         
     | 
| 
      
 332 
     | 
    
         
            +
                    cipher_name       = decryption_cipher.cipher_name
         
     | 
| 
      
 333 
     | 
    
         
            +
                  end
         
     | 
| 
       323 
334 
     | 
    
         | 
| 
       324 
     | 
    
         
            -
                  @stream_cipher = ::OpenSSL::Cipher.new(cipher_name 
     | 
| 
      
 335 
     | 
    
         
            +
                  @stream_cipher = ::OpenSSL::Cipher.new(cipher_name)
         
     | 
| 
       325 
336 
     | 
    
         
             
                  @stream_cipher.decrypt
         
     | 
| 
       326 
337 
     | 
    
         
             
                  @stream_cipher.key = key || decryption_cipher.send(:key)
         
     | 
| 
       327 
     | 
    
         
            -
                  @stream_cipher.iv = iv || decryption_cipher. 
     | 
| 
      
 338 
     | 
    
         
            +
                  @stream_cipher.iv = iv || decryption_cipher.iv
         
     | 
| 
       328 
339 
     | 
    
         | 
| 
       329 
340 
     | 
    
         
             
                  # First call to #update should return an empty string anyway
         
     | 
| 
       330 
341 
     | 
    
         
             
                  if buf && buf.length > 0
         
     |