Class: Archive::Zip::Codec::TraditionalEncryption::Decrypt
- Includes:
- IO::Like
- Defined in:
- lib/archive/zip/codec/traditional_encryption.rb
Overview
Archive::Zip::Codec::TraditionalEncryption::Decrypt is a readable, IO-like object which decrypts data data it reads from a delegate IO object using the traditional encryption algorithm as documented in the ZIP specification. A close method is also provided which can optionally close the delegate object.
Instances of this class should only be accessed via the Archive::Zip::Codec::TraditionalEncryption#decompressor method.
Instance Attribute Summary
Attributes inherited from Base
Class Method Summary collapse
-
.open(io, password, mtime) ⇒ Object
Creates a new instance of this class with the given argument using #new and then passes the instance to the given block.
Instance Method Summary collapse
-
#close(close_delegate = true) ⇒ Object
Closes this object so that further write operations will fail.
-
#initialize(io, password, mtime) ⇒ Decrypt
constructor
Creates a new instance of this class using io as a data source.
-
#initialize_keys ⇒ Object
private
Extend the inherited initialize_keys method to further initialize the keys by encrypting and writing a 12 byte header to the delegate IO object.
-
#unbuffered_read(length) ⇒ Object
private
Reads, decrypts, and returns at most length bytes from the delegate IO object.
-
#unbuffered_seek(offset, whence = IO::SEEK_SET) ⇒ Object
private
Allows resetting this object and the delegate object back to the beginning of the stream or reporting the current position in the stream.
Methods inherited from Base
Constructor Details
#initialize(io, password, mtime) ⇒ Decrypt
Creates a new instance of this class using io as a data source. io must be readable and provide a read method as an IO instance would or errors will be raised when performing read operations. password should be the encryption key. mtime must be the last modified time of the entry to be encrypted/decrypted.
This class has extremely limited seek capabilities. It is possible to seek with an offset of 0
and a whence of IO::SEEK_CUR
. As a result, the pos and tell methods also work as expected.
Due to certain optimizations within IO::Like#seek and if there is data in the read buffer, the seek method can be used to seek forward from the current stream position up to the end of the buffer. Unless it is known definitively how much data is in the buffer, it is best to avoid relying on this behavior.
If io also responds to rewind, then the rewind method of this class can be used to reset the whole stream back to the beginning. Using seek of this class to seek directly to offset 0
using IO::SEEK_SET
for whence will also work in this case.
Any other seeking attempts, will raise Errno::EINVAL exceptions.
The fill_size attribute is set to 0
by default under the assumption that io is already buffered.
277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 277 def initialize(io, password, mtime) # Keep track of the total number of bytes read. # Set this here so that the call to #initialize_keys caused by the call # to super below does not cause errors in #unbuffered_read due to this # attribute being uninitialized. @total_bytes_out = 0 super(io, password, mtime) # Assume that the delegate IO object is already buffered. self.fill_size = 0 end |
Class Method Details
.open(io, password, mtime) ⇒ Object
Creates a new instance of this class with the given argument using #new and then passes the instance to the given block. The #close method is guaranteed to be called after the block completes.
Equivalent to #new if no block is given.
241 242 243 244 245 246 247 248 249 250 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 241 def self.open(io, password, mtime) decrypt_io = new(io, password, mtime) return decrypt_io unless block_given? begin yield(decrypt_io) ensure decrypt_io.close unless decrypt_io.closed? end end |
Instance Method Details
#close(close_delegate = true) ⇒ Object
Closes this object so that further write operations will fail. If close_delegate is true
, the delegate object used as a data source will also be closed using its close method.
293 294 295 296 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 293 def close(close_delegate = true) super() io.close if close_delegate end |
#initialize_keys ⇒ Object (private)
Extend the inherited initialize_keys method to further initialize the keys by encrypting and writing a 12 byte header to the delegate IO object.
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 303 def initialize_keys super # Load the 12 byte header taking care to ensure that all bytes are read. bytes_needed = 12 while bytes_needed > 0 do begin bytes_read = unbuffered_read(bytes_needed) bytes_needed -= bytes_read.size rescue Errno::EAGAIN, Errno::EINTR sleep(1) end end # Reset the total bytes read in order to disregard the header. @total_bytes_out = 0 nil end |
#unbuffered_read(length) ⇒ Object (private)
Reads, decrypts, and returns at most length bytes from the delegate IO object.
Raises EOFError if there is no data to read.
327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 327 def unbuffered_read(length) buffer = io.read(length) raise EOFError, 'end of file reached' if buffer.nil? @total_bytes_out += buffer.length 0.upto(buffer.length - 1) do |i| buffer[i] = (buffer[i].ord ^ decrypt_byte).chr update_keys(buffer[i].chr) end buffer end |
#unbuffered_seek(offset, whence = IO::SEEK_SET) ⇒ Object (private)
Allows resetting this object and the delegate object back to the beginning of the stream or reporting the current position in the stream.
Raises Errno::EINVAL unless offset is 0
and whence is either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if whence is IO::SEEK_SEK and the delegate object does not respond to the rewind method.
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 346 def unbuffered_seek(offset, whence = IO::SEEK_SET) unless offset == 0 && ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) || whence == IO::SEEK_CUR) then raise Errno::EINVAL end case whence when IO::SEEK_SET io.rewind initialize_keys @total_bytes_out = 0 when IO::SEEK_CUR @total_bytes_out end end |