lib/symmetric_encryption/cipher.rb in symmetric-encryption-3.0.3 vs lib/symmetric_encryption/cipher.rb in symmetric-encryption-3.1.0

- old
+ new

@@ -17,11 +17,10 @@ alias_method :cipher, :cipher_name # Defines the Header Structure returned when parsing the header HeaderStruct = Struct.new( :compressed, # [true|false] Whether the data is compressed, if supplied in the header - :binary, # [true|false] Whether the data is binary, if supplied in the header :iv, # [String] IV used to encrypt the data, if supplied in the header :key, # [String] Key used to encrypt the data, if supplied in the header :cipher_name, # [String] Name of the cipher used, if supplied in the header :version, # [Integer] Version of the cipher used, if supplied in the header :decryption_cipher, # [SymmetricEncryption::Cipher] Cipher matching the header, or SymmetricEncryption.cipher(default_version) @@ -98,11 +97,11 @@ raise "Invalid Encoding: #{@encoding}" unless ENCODINGS.include?(@encoding) 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) parms.each_pair {|k,v| warn "SymmetricEncryption::Cipher Ignoring unknown option #{k.inspect} = #{v.inspect}"} end - # Encrypt and then encode a binary or UTF-8 string + # Encrypt and then encode a string # # Returns data encrypted and then encoded according to the encoding setting # of this cipher # Returns nil if str is nil # Returns "" str is empty @@ -152,30 +151,30 @@ # # Parameters # encrypted_string [String] # Binary encrypted string to decrypt # - # header [HeaderStruct] - # Optional header for the supplied encrypted_string + # Reads the header if present for key, iv, cipher_name and compression # - # binary [true|false] - # If no header is supplied then determines whether the string returned - # is binary or UTF8 - # - # Reads the 'magic' header if present for key, iv, cipher_name and compression - # # encrypted_string must be in raw binary form when calling this method # # Creates a new OpenSSL::Cipher with every call so that this call # is thread-safe and can be called concurrently by multiple threads with # the same instance of Cipher def decrypt(str) decoded = self.decode(str) return unless decoded return decoded if decoded.empty? - binary_decrypt(decoded).force_encoding(SymmetricEncryption::UTF8_ENCODING) + decrypted = binary_decrypt(decoded) + if defined?(Encoding) + # Try to force result to UTF-8 encoding, but if it is not valid, force it back to Binary + unless decrypted.force_encoding(SymmetricEncryption::UTF8_ENCODING).valid_encoding? + decrypted.force_encoding(SymmetricEncryption::BINARY_ENCODING) + end + end + decrypted end # Returns UTF8 encoded string after encoding the supplied Binary string # # Encode the supplied string using the encoding in this cipher instance @@ -281,11 +280,10 @@ _, flags = buffer.slice!(0..MAGIC_HEADER_SIZE+1).unpack(MAGIC_HEADER_UNPACK) compressed = (flags & 0b1000_0000_0000_0000) != 0 include_iv = (flags & 0b0100_0000_0000_0000) != 0 include_key = (flags & 0b0010_0000_0000_0000) != 0 include_cipher= (flags & 0b0001_0000_0000_0000) != 0 - binary = (flags & 0b0000_1000_0000_0000) != 0 # Version of the key to use to decrypt the key if present, # otherwise to decrypt the data following the header version = flags & 0b0000_0000_1111_1111 decryption_cipher = SymmetricEncryption.cipher(version) raise "Cipher with version:#{version.inspect} not found in any of the configured SymmetricEncryption ciphers" unless decryption_cipher @@ -295,18 +293,18 @@ len = buffer.slice!(0..1).unpack('v').first iv = buffer.slice!(0..len-1) end if include_key len = buffer.slice!(0..1).unpack('v').first - key = decryption_cipher.binary_decrypt(buffer.slice!(0..len-1), header=false, binary=true) + key = decryption_cipher.binary_decrypt(buffer.slice!(0..len-1), header=false) end if include_cipher len = buffer.slice!(0..1).unpack('v').first cipher_name = buffer.slice!(0..len-1) end - HeaderStruct.new(compressed, binary, iv, key, cipher_name, version, decryption_cipher) + HeaderStruct.new(compressed, iv, key, cipher_name, version, decryption_cipher) end # Returns a magic header for this cipher instance that can be placed at # the beginning of a file or stream to indicate how the data was encrypted # @@ -326,15 +324,11 @@ # # cipher_name # Includes the cipher_name used. For example 'aes-256-cbc' # The cipher_name string to to put in the header # Default: nil : Exclude cipher_name name from header - # - # binary - # Whether the data being encrypted is binary. - # When the header is read, it sets the encoding of the string returned to Binary - def self.build_header(version, compressed=false, iv=nil, key=nil, cipher_name=nil, binary=false) + def self.build_header(version, compressed=false, iv=nil, key=nil, cipher_name=nil) # Ruby V2 named parameters would be perfect here # Version number of supplied encryption key, or use the global cipher version if none was supplied flags = iv || key ? (SymmetricEncryption.cipher.version || 0) : (version || 0) # Same as 0b0000_0000_0000_0000 @@ -342,11 +336,10 @@ # compressed bit in the flags word flags |= 0b1000_0000_0000_0000 if compressed flags |= 0b0100_0000_0000_0000 if iv flags |= 0b0010_0000_0000_0000 if key flags |= 0b0001_0000_0000_0000 if cipher_name - flags |= 0b0000_1000_0000_0000 if binary header = "#{MAGIC_HEADER}#{[flags].pack('v')}".force_encoding(SymmetricEncryption::BINARY_ENCODING) if iv header << [iv.length].pack('v') header << iv end @@ -389,12 +382,11 @@ result = if add_header # Random iv and compress both add the magic header iv = random_iv ? openssl_cipher.random_iv : @iv openssl_cipher.iv = iv if iv # Set the binary indicator on the header if string is Binary Encoded - binary = (string.encoding == SymmetricEncryption::BINARY_ENCODING) - self.class.build_header(version, compress, random_iv ? iv : nil, nil, nil, binary) + + self.class.build_header(version, compress, random_iv ? iv : nil, nil, nil) + openssl_cipher.update(compress ? Zlib::Deflate.deflate(string) : string) else openssl_cipher.iv = @iv if @iv openssl_cipher.update(string) end @@ -403,10 +395,11 @@ # Advanced use only # See #decrypt to decrypt encoded strings # # Returns a Binary decrypted string without decoding the string first + # The returned string has BINARY encoding # # Decryption of supplied string # Returns the decrypted string # Returns nil if encrypted_string is nil # Returns '' if encrypted_string == '' @@ -416,14 +409,10 @@ # Binary encrypted string to decrypt # # header [HeaderStruct] # Optional header for the supplied encrypted_string # - # binary [true|false] - # If no header is supplied then determines whether the string returned - # is binary or UTF8 - # # Reads the 'magic' header if present for key, iv, cipher_name and compression # # encrypted_string must be in raw binary form when calling this method # # Creates a new OpenSSL::Cipher with every call so that this call @@ -431,20 +420,19 @@ # the same instance of Cipher # # Note: # When a string is encrypted and the header is used, its decrypted form # is automatically set to the same UTF-8 or Binary encoding - def binary_decrypt(encrypted_string, header=nil, binary=false) + def binary_decrypt(encrypted_string, header=nil) return if encrypted_string.nil? str = encrypted_string.to_s str.force_encoding(SymmetricEncryption::BINARY_ENCODING) if str.respond_to?(:force_encoding) return str if str.empty? - decrypted_string = if header || self.class.has_header?(str) + if header || self.class.has_header?(str) str = str.dup header ||= self.class.parse_header!(str) - binary = header.binary openssl_cipher = ::OpenSSL::Cipher.new(header.cipher_name || self.cipher_name) openssl_cipher.decrypt openssl_cipher.key = header.key || @key iv = header.iv || @iv @@ -458,22 +446,14 @@ openssl_cipher.key = @key openssl_cipher.iv = @iv if @iv result = openssl_cipher.update(str) result << openssl_cipher.final end - - # Support Ruby 1.9 and above Encoding - if defined?(Encoding) - # Sets the encoding of the result string to UTF8 or BINARY based on the binary header - binary ? decrypted_string.force_encoding(SymmetricEncryption::BINARY_ENCODING) : decrypted_string.force_encoding(SymmetricEncryption::UTF8_ENCODING) - else - decrypted_string - end end # Returns [String] object represented as a string, filtering out the key def inspect - "#<#{self.class}:0x#{self.__id__.to_s(16)} @key=\"[FILTERED]\" @iv=#{iv.inspect} @cipher_name=#{cipher_name.inspect}, @version=#{version.inspect}, @encoding=#{encoding.inspect}" + "#<#{self.class}:0x#{self.__id__.to_s(16)} @key=\"[FILTERED]\" @iv=#{iv.inspect} @cipher_name=#{cipher_name.inspect}, @version=#{version.inspect}, @encoding=#{encoding.inspect}, @always_add_header=#{always_add_header.inspect}" end private attr_reader :key