lib/symmetric_encryption/reader.rb in symmetric-encryption-0.6.0 vs lib/symmetric_encryption/reader.rb in symmetric-encryption-0.6.1
- old
+ new
@@ -65,12 +65,11 @@
#
# # Example: Reading from a CSV file
#
# require 'fastercsv'
# begin
- # # Must supply :row_sep for FasterCSV otherwise it will attempt to rewind the file etc.
- # csv = FasterCSV.new(SymmetricEncryption::Reader.open('csv_encrypted'), :row_sep => "\n")
+ # csv = FasterCSV.new(SymmetricEncryption::Reader.open('csv_encrypted'))
# csv.each {|row| p row}
# ensure
# csv.close if csv
# end
def self.open(filename_or_stream, options={}, &block)
@@ -90,33 +89,12 @@
# Decrypt data before reading from the supplied stream
def initialize(ios,options={})
@ios = ios
@buffer_size = options.fetch(:buffer_size, 4096).to_i
- @compressed = nil
- @read_buffer = ''
-
- # Read first block and check for the header
- buf = @ios.read(@buffer_size)
- if buf.start_with?(SymmetricEncryption::MAGIC_HEADER)
- # Header includes magic header and version byte
- # Remove header and extract flags
- header, flags = buf.slice!(0..MAGIC_HEADER_SIZE+1).unpack(MAGIC_HEADER_UNPACK)
- @compressed = (flags & 0b1000_0000_0000_0000) != 0
- @version = @compressed ? flags - 0b1000_0000_0000_0000 : flags
- else
- @version = options[:version]
- end
-
- # Use primary cipher by default, but allow a secondary cipher to be selected for encryption
- @cipher = SymmetricEncryption.cipher(@version)
- raise "Cipher with version:#{@version} not found in any of the configured SymmetricEncryption ciphers" unless @cipher
- @stream_cipher = @cipher.send(:openssl_cipher, :decrypt)
-
- # First call to #update should return an empty string anyway
- @read_buffer << @stream_cipher.update(buf)
- @read_buffer << @stream_cipher.final if @ios.eof?
+ @version = options[:version]
+ read_header
end
# Returns whether the stream being read is compressed
#
# Should be called before any reads are performed to determine if the file or
@@ -184,10 +162,11 @@
buf = @ios.read || ''
data << @stream_cipher.update(buf) if buf && buf.length > 0
data << @stream_cipher.final
end
end
+ @pos += data.length
data
end
# Reads a single decrypted line from the file up to and including the optional sep_string.
# Raises EOFError on eof
@@ -207,10 +186,11 @@
while (index = @read_buffer.index(sep_string)).nil? && !@ios.eof?
read_block
end
index ||= -1
data = @read_buffer.slice!(0..index)
+ @pos += data.length
return nil if data.length == 0 && eof?
data
end
# ios.each(sep_string="\n") {|line| block } => ios
@@ -229,23 +209,47 @@
# Returns whether the end of file has been reached for this stream
def eof?
(@read_buffer.size == 0) && @ios.eof?
end
- # Return the approximate offset in bytes of the current input stream
- # Since the encrypted data size does not match the unencrypted size
- # this value cannot be guaranteed. Especially if compression is turned on
+ # Return the number of bytes read so far from the input stream
def pos
- @ios.pos - @read_buffer.size
+ @pos
end
# Rewind back to the beginning of the file
def rewind
@read_buffer = ''
@ios.rewind
+ read_header
end
private
+
+ # Read the header from the file if present
+ def read_header
+ @compressed = nil
+ @pos = 0
+
+ # Read first block and check for the header
+ buf = @ios.read(@buffer_size)
+ if buf.start_with?(MAGIC_HEADER)
+ # Header includes magic header and version byte
+ # Remove header and extract flags
+ header, flags = buf.slice!(0..MAGIC_HEADER_SIZE+1).unpack(MAGIC_HEADER_UNPACK)
+ @compressed = (flags & 0b1000_0000_0000_0000) != 0
+ @version = @compressed ? flags - 0b1000_0000_0000_0000 : flags
+ end
+
+ # Use primary cipher by default, but allow a secondary cipher to be selected for encryption
+ @cipher = SymmetricEncryption.cipher(@version)
+ raise "Cipher with version:#{@version.inspect} not found in any of the configured SymmetricEncryption ciphers" unless @cipher
+ @stream_cipher = @cipher.send(:openssl_cipher, :decrypt)
+
+ # First call to #update should return an empty string anyway
+ @read_buffer = @stream_cipher.update(buf)
+ @read_buffer << @stream_cipher.final if @ios.eof?
+ end
# Read a block of data and append the decrypted data in the read buffer
def read_block
buf = @ios.read(@buffer_size)
@read_buffer << @stream_cipher.update(buf) if buf && buf.length > 0