Class: Archive::Zip::Codec::TraditionalEncryption::Encrypt
- Includes:
- IO::Like
- Defined in:
- lib/archive/zip/codec/traditional_encryption.rb
Overview
Archive::Zip::Codec::TraditionalEncryption::Encrypt is a writable, IO-like object which encrypts data written to it using the traditional encryption algorithm as documented in the ZIP specification and writes the result to a delegate IO object. 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#compressor 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 the stream after flushing the encryption buffer to the delegate.
-
#initialize(io, password, mtime) ⇒ Encrypt
constructor
Creates a new instance of this class using io as a data sink.
-
#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_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.
-
#unbuffered_write(string) ⇒ Object
private
Encrypts and writes string to the delegate IO object.
Methods inherited from Base
Constructor Details
#initialize(io, password, mtime) ⇒ Encrypt
Creates a new instance of this class using io as a data sink. io must be writable and must provide a write method as IO does or errors will be raised when performing write operations. password should be the encryption key. mtime must be the last modified time of the entry to be encrypted/decrypted.
The flush_size attribute is set to 0
by default under the assumption that io is already buffered.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 109 def initialize(io, password, mtime) # Keep track of the total number of bytes written. # Set this here so that the call to #initialize_keys caused by the call # to super below does not cause errors in #unbuffered_write due to this # attribute being uninitialized. @total_bytes_in = 0 # This buffer is used to hold the encrypted version of the string most # recently sent to #unbuffered_write. @encrypt_buffer = '' super(io, password, mtime) # Assume that the delegate IO object is already buffered. self.flush_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.
90 91 92 93 94 95 96 97 98 99 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 90 def self.open(io, password, mtime) encrypt_io = new(io, password, mtime) return encrypt_io unless block_given? begin yield(encrypt_io) ensure encrypt_io.close unless encrypt_io.closed? end end |
Instance Method Details
#close(close_delegate = true) ⇒ Object
Closes the stream after flushing the encryption buffer to the delegate. If close_delegate is true
, the delegate object used as a data sink will also be closed using its close method.
Raises IOError if called more than once.
131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 131 def close(close_delegate = true) flush() begin until @encrypt_buffer.empty? do @encrypt_buffer.slice!(0, io.write(@encrypt_buffer)) end rescue Errno::EAGAIN, Errno::EINTR retry if write_ready? end super() io.close if close_delegate nil 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.
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 151 def initialize_keys super # Create and encrypt a 12 byte header to protect the encrypted file data # from attack. The first 10 bytes are random, and the last 2 bytes are # the low order word in little endian byte order of the last modified # time of the entry in DOS format. header = '' 10.times do header << rand(256).chr end header << mtime.to_dos_time.pack[0, 2] # Take care to ensure that all bytes in the header are written. while header.size > 0 do begin header.slice!(0, unbuffered_write(header)) rescue Errno::EAGAIN, Errno::EINTR sleep(1) end end # Reset the total bytes written in order to disregard the header. @total_bytes_in = 0 nil 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.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 186 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 @encrypt_buffer = '' initialize_keys @total_bytes_in = 0 when IO::SEEK_CUR @total_bytes_in end end |
#unbuffered_write(string) ⇒ Object (private)
Encrypts and writes string to the delegate IO object. Returns the number of bytes of string written.
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/archive/zip/codec/traditional_encryption.rb', line 206 def unbuffered_write(string) # First try to write out the contents of the encrypt buffer because if # that raises a failure we can let that pass up the call stack without # having polluted the encryption state. until @encrypt_buffer.empty? do @encrypt_buffer.slice!(0, io.write(@encrypt_buffer)) end # At this point we can encrypt the given string into a new buffer and # behave as if it was written. string.each_byte do |byte| temp = decrypt_byte @encrypt_buffer << (byte ^ temp).chr update_keys(byte.chr) end @total_bytes_in += string.length string.length end |