archive-zip 0.12.0 → 0.13.0.pre1

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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/NEWS.md +5 -0
  4. data/README.md +1 -1
  5. data/lib/archive/support/ioextensions.rb +5 -7
  6. data/lib/archive/support/iowindow.rb +34 -87
  7. data/lib/archive/support/stringio.rb +27 -0
  8. data/lib/archive/support/zlib.rb +0 -435
  9. data/lib/archive/zip/codec/deflate/reader.rb +187 -0
  10. data/lib/archive/zip/codec/deflate/writer.rb +209 -0
  11. data/lib/archive/zip/codec/deflate.rb +92 -244
  12. data/lib/archive/zip/codec/null_encryption.rb +4 -184
  13. data/lib/archive/zip/codec/store/reader.rb +97 -0
  14. data/lib/archive/zip/codec/store/writer.rb +78 -0
  15. data/lib/archive/zip/codec/store.rb +6 -231
  16. data/lib/archive/zip/codec/traditional_encryption/base.rb +85 -0
  17. data/lib/archive/zip/codec/traditional_encryption/reader.rb +65 -0
  18. data/lib/archive/zip/codec/traditional_encryption/writer.rb +71 -0
  19. data/lib/archive/zip/codec/traditional_encryption.rb +6 -360
  20. data/lib/archive/zip/dos_time.rb +103 -0
  21. data/lib/archive/zip/entry.rb +86 -70
  22. data/lib/archive/zip.rb +2 -5
  23. metadata +35 -227
  24. data/.yardopts +0 -1
  25. data/Rakefile +0 -247
  26. data/lib/archive/support/binary_stringio.rb +0 -30
  27. data/lib/archive/support/integer.rb +0 -13
  28. data/lib/archive/support/io-like.rb +0 -14
  29. data/lib/archive/support/time.rb +0 -119
  30. data/lib/archive/zip/version.rb +0 -6
  31. data/spec/archive/dos_time_spec.rb +0 -113
  32. data/spec/archive/zip/archive_spec.rb +0 -54
  33. data/spec/archive/zip/codec/deflate/compress/checksum_spec.rb +0 -44
  34. data/spec/archive/zip/codec/deflate/compress/close_spec.rb +0 -45
  35. data/spec/archive/zip/codec/deflate/compress/crc32_spec.rb +0 -23
  36. data/spec/archive/zip/codec/deflate/compress/data_descriptor_spec.rb +0 -74
  37. data/spec/archive/zip/codec/deflate/compress/new_spec.rb +0 -39
  38. data/spec/archive/zip/codec/deflate/compress/open_spec.rb +0 -48
  39. data/spec/archive/zip/codec/deflate/compress/write_spec.rb +0 -111
  40. data/spec/archive/zip/codec/deflate/decompress/checksum_spec.rb +0 -20
  41. data/spec/archive/zip/codec/deflate/decompress/close_spec.rb +0 -34
  42. data/spec/archive/zip/codec/deflate/decompress/crc32_spec.rb +0 -20
  43. data/spec/archive/zip/codec/deflate/decompress/data_descriptor_spec.rb +0 -74
  44. data/spec/archive/zip/codec/deflate/decompress/new_spec.rb +0 -16
  45. data/spec/archive/zip/codec/deflate/decompress/open_spec.rb +0 -29
  46. data/spec/archive/zip/codec/deflate/fixtures/classes.rb +0 -25
  47. data/spec/archive/zip/codec/deflate/fixtures/compressed_file.bin +0 -1
  48. data/spec/archive/zip/codec/deflate/fixtures/compressed_file_nocomp.bin +0 -0
  49. data/spec/archive/zip/codec/deflate/fixtures/raw_file.txt +0 -10
  50. data/spec/archive/zip/codec/null_encryption/decrypt/close_spec.rb +0 -34
  51. data/spec/archive/zip/codec/null_encryption/decrypt/new_spec.rb +0 -16
  52. data/spec/archive/zip/codec/null_encryption/decrypt/open_spec.rb +0 -29
  53. data/spec/archive/zip/codec/null_encryption/decrypt/read_spec.rb +0 -26
  54. data/spec/archive/zip/codec/null_encryption/decrypt/rewind_spec.rb +0 -27
  55. data/spec/archive/zip/codec/null_encryption/decrypt/seek_spec.rb +0 -59
  56. data/spec/archive/zip/codec/null_encryption/decrypt/tell_spec.rb +0 -23
  57. data/spec/archive/zip/codec/null_encryption/encrypt/close_spec.rb +0 -34
  58. data/spec/archive/zip/codec/null_encryption/encrypt/new_spec.rb +0 -16
  59. data/spec/archive/zip/codec/null_encryption/encrypt/open_spec.rb +0 -31
  60. data/spec/archive/zip/codec/null_encryption/encrypt/rewind_spec.rb +0 -28
  61. data/spec/archive/zip/codec/null_encryption/encrypt/seek_spec.rb +0 -52
  62. data/spec/archive/zip/codec/null_encryption/encrypt/tell_spec.rb +0 -31
  63. data/spec/archive/zip/codec/null_encryption/encrypt/write_spec.rb +0 -31
  64. data/spec/archive/zip/codec/null_encryption/fixtures/classes.rb +0 -12
  65. data/spec/archive/zip/codec/null_encryption/fixtures/raw_file.txt +0 -10
  66. data/spec/archive/zip/codec/store/compress/close_spec.rb +0 -34
  67. data/spec/archive/zip/codec/store/compress/data_descriptor_spec.rb +0 -77
  68. data/spec/archive/zip/codec/store/compress/new_spec.rb +0 -16
  69. data/spec/archive/zip/codec/store/compress/open_spec.rb +0 -29
  70. data/spec/archive/zip/codec/store/compress/rewind_spec.rb +0 -28
  71. data/spec/archive/zip/codec/store/compress/seek_spec.rb +0 -52
  72. data/spec/archive/zip/codec/store/compress/tell_spec.rb +0 -31
  73. data/spec/archive/zip/codec/store/compress/write_spec.rb +0 -29
  74. data/spec/archive/zip/codec/store/decompress/close_spec.rb +0 -34
  75. data/spec/archive/zip/codec/store/decompress/data_descriptor_spec.rb +0 -75
  76. data/spec/archive/zip/codec/store/decompress/new_spec.rb +0 -16
  77. data/spec/archive/zip/codec/store/decompress/open_spec.rb +0 -29
  78. data/spec/archive/zip/codec/store/decompress/read_spec.rb +0 -26
  79. data/spec/archive/zip/codec/store/decompress/rewind_spec.rb +0 -27
  80. data/spec/archive/zip/codec/store/decompress/seek_spec.rb +0 -59
  81. data/spec/archive/zip/codec/store/decompress/tell_spec.rb +0 -23
  82. data/spec/archive/zip/codec/store/fixtures/classes.rb +0 -12
  83. data/spec/archive/zip/codec/store/fixtures/raw_file.txt +0 -10
  84. data/spec/archive/zip/codec/traditional_encryption/decrypt/close_spec.rb +0 -53
  85. data/spec/archive/zip/codec/traditional_encryption/decrypt/new_spec.rb +0 -20
  86. data/spec/archive/zip/codec/traditional_encryption/decrypt/open_spec.rb +0 -43
  87. data/spec/archive/zip/codec/traditional_encryption/decrypt/read_spec.rb +0 -127
  88. data/spec/archive/zip/codec/traditional_encryption/decrypt/rewind_spec.rb +0 -36
  89. data/spec/archive/zip/codec/traditional_encryption/decrypt/seek_spec.rb +0 -80
  90. data/spec/archive/zip/codec/traditional_encryption/decrypt/tell_spec.rb +0 -27
  91. data/spec/archive/zip/codec/traditional_encryption/encrypt/close_spec.rb +0 -53
  92. data/spec/archive/zip/codec/traditional_encryption/encrypt/new_spec.rb +0 -20
  93. data/spec/archive/zip/codec/traditional_encryption/encrypt/open_spec.rb +0 -41
  94. data/spec/archive/zip/codec/traditional_encryption/encrypt/rewind_spec.rb +0 -39
  95. data/spec/archive/zip/codec/traditional_encryption/encrypt/seek_spec.rb +0 -73
  96. data/spec/archive/zip/codec/traditional_encryption/encrypt/tell_spec.rb +0 -40
  97. data/spec/archive/zip/codec/traditional_encryption/encrypt/write_spec.rb +0 -114
  98. data/spec/archive/zip/codec/traditional_encryption/fixtures/classes.rb +0 -27
  99. data/spec/archive/zip/codec/traditional_encryption/fixtures/encrypted_file.bin +0 -0
  100. data/spec/archive/zip/codec/traditional_encryption/fixtures/raw_file.txt +0 -10
  101. data/spec/binary_stringio/new_spec.rb +0 -40
  102. data/spec/binary_stringio/set_encoding_spec.rb +0 -17
  103. data/spec/ioextensions/read_exactly_spec.rb +0 -52
  104. data/spec/zlib/fixtures/classes.rb +0 -65
  105. data/spec/zlib/fixtures/compressed_file.bin +0 -1
  106. data/spec/zlib/fixtures/compressed_file_gzip.bin +0 -0
  107. data/spec/zlib/fixtures/compressed_file_huffman.bin +0 -2
  108. data/spec/zlib/fixtures/compressed_file_minmem.bin +0 -0
  109. data/spec/zlib/fixtures/compressed_file_minwin.bin +0 -1
  110. data/spec/zlib/fixtures/compressed_file_nocomp.bin +0 -0
  111. data/spec/zlib/fixtures/compressed_file_raw.bin +0 -1
  112. data/spec/zlib/fixtures/raw_file.txt +0 -10
  113. data/spec/zlib/zreader/checksum_spec.rb +0 -42
  114. data/spec/zlib/zreader/close_spec.rb +0 -16
  115. data/spec/zlib/zreader/compressed_size_spec.rb +0 -20
  116. data/spec/zlib/zreader/new_spec.rb +0 -43
  117. data/spec/zlib/zreader/open_spec.rb +0 -51
  118. data/spec/zlib/zreader/read_spec.rb +0 -58
  119. data/spec/zlib/zreader/rewind_spec.rb +0 -25
  120. data/spec/zlib/zreader/seek_spec.rb +0 -57
  121. data/spec/zlib/zreader/tell_spec.rb +0 -23
  122. data/spec/zlib/zreader/uncompressed_size_spec.rb +0 -20
  123. data/spec/zlib/zwriter/checksum_spec.rb +0 -43
  124. data/spec/zlib/zwriter/close_spec.rb +0 -16
  125. data/spec/zlib/zwriter/compressed_size_spec.rb +0 -21
  126. data/spec/zlib/zwriter/new_spec.rb +0 -66
  127. data/spec/zlib/zwriter/open_spec.rb +0 -70
  128. data/spec/zlib/zwriter/rewind_spec.rb +0 -28
  129. data/spec/zlib/zwriter/seek_spec.rb +0 -52
  130. data/spec/zlib/zwriter/tell_spec.rb +0 -31
  131. data/spec/zlib/zwriter/uncompressed_size_spec.rb +0 -21
  132. data/spec/zlib/zwriter/write_spec.rb +0 -28
@@ -0,0 +1,85 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'io/like_helpers/delegated_io'
4
+
5
+ require 'archive/support/zlib'
6
+
7
+ module Archive; class Zip; module Codec; class TraditionalEncryption
8
+ # Archive::Zip::Codec::TraditionalEncryption::Base provides some basic
9
+ # methods which are shared between
10
+ # Archive::Zip::Codec::TraditionalEncryption::Writer and
11
+ # Archive::Zip::Codec::TraditionalEncryption::Reader.
12
+ #
13
+ # Do not use this class directly.
14
+ class Base < IO::LikeHelpers::DelegatedIO
15
+ # Creates a new instance of this class. _delegate must be an IO-like
16
+ # object to be used as a delegate for IO operations. _password_ should be
17
+ # the encryption key. _mtime_ must be the last modified time of the entry
18
+ # to be encrypted/decrypted.
19
+ def initialize(delegate, password, mtime, autoclose: true)
20
+ super(delegate, autoclose: autoclose)
21
+ @password = password
22
+ @mtime = mtime
23
+
24
+ initialize_keys
25
+ end
26
+
27
+ # Allows resetting this object and the delegate object back to the
28
+ # beginning of the stream or reporting the current position in the stream.
29
+ #
30
+ # Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
31
+ # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
32
+ # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
33
+ # method.
34
+ def seek(amount, whence = IO::SEEK_SET)
35
+ assert_open
36
+ raise Errno::ESPIPE if amount != 0 || whence == IO::SEEK_END
37
+
38
+ case whence
39
+ when IO::SEEK_SET
40
+ result = super
41
+ return result if Symbol === result
42
+ initialize_keys
43
+ result
44
+ when IO::SEEK_CUR
45
+ @bytes_processed
46
+ else
47
+ raise Errno::EINVAL
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ # Initializes the keys used for encrypting/decrypting data by setting the
54
+ # keys to well known values and then processing them with the password.
55
+ def initialize_keys
56
+ @key0 = 0x12345678
57
+ @key1 = 0x23456789
58
+ @key2 = 0x34567890
59
+ @password.each_char { |char| update_keys(char) }
60
+
61
+ @bytes_processed = 0
62
+ nil
63
+ end
64
+
65
+ # Updates the keys following the ZIP specification using _char_, which
66
+ # must be a single byte String.
67
+ def update_keys(char)
68
+ # For some reason not explained in the ZIP specification but discovered
69
+ # in the source for InfoZIP, the old CRC value must first have its bits
70
+ # flipped before processing. The new CRC value must have its bits
71
+ # flipped as well for storage and later use. This applies to the
72
+ # handling of @key0 and @key2.
73
+ @key0 = ~Zlib.crc32(char, ~@key0)
74
+ @key1 = ((@key1 + (@key0 & 0xff)) * 134775813 + 1) & 0xffffffff
75
+ @key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
76
+ nil
77
+ end
78
+
79
+ # Returns the next decryption byte based on the current keys.
80
+ def decrypt_byte
81
+ temp = (@key2 | 2) & 0x0000ffff
82
+ ((temp * (temp ^ 1)) >> 8) & 0x000000ff
83
+ end
84
+ end
85
+ end; end; end; end
@@ -0,0 +1,65 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'archive/zip/codec/traditional_encryption/base'
4
+
5
+ module Archive; class Zip; module Codec; class TraditionalEncryption
6
+ # Archive::Zip::Codec::TraditionalEncryption::Reader is a readable, IO-like
7
+ # object which decrypts data data it reads from a delegate IO object using
8
+ # the traditional encryption algorithm as documented in the ZIP
9
+ # specification. A _close_ method is also provided which can optionally
10
+ # close the delegate object.
11
+ #
12
+ # Instances of this class should only be accessed via the
13
+ # Archive::Zip::Codec::TraditionalEncryption#decompressor method.
14
+ class Reader < Base
15
+ # Reads, decrypts, and returns at most _length_ bytes from the delegate IO
16
+ # object.
17
+ #
18
+ # Raises EOFError if there is no data to read.
19
+ def read(length, buffer: nil, buffer_offset: 0)
20
+ # This short circuits if the header has already been read.
21
+ result = read_header
22
+ return result if Symbol === result
23
+
24
+ result = super
25
+ return result if Symbol === result
26
+
27
+ if buffer.nil?
28
+ buffer = result
29
+ buffer_offset = 0
30
+ length = buffer.bytesize
31
+ else
32
+ length = result
33
+ end
34
+
35
+ buffer[buffer_offset, length].to_enum(:each_byte).each_with_index do |byte, idx|
36
+ buffer[idx] = (byte ^ decrypt_byte).chr
37
+ update_keys(buffer[idx])
38
+ end
39
+ @bytes_processed += length
40
+
41
+ result
42
+ end
43
+
44
+ private
45
+
46
+ def read_header
47
+ while @header_bytes_needed > 0 do
48
+ result = delegate.read(@header_bytes_needed)
49
+ return result if Symbol === result
50
+
51
+ result.each_byte do |byte|
52
+ update_keys((byte ^ decrypt_byte).chr)
53
+ end
54
+ @header_bytes_needed -= result.bytesize
55
+ end
56
+
57
+ nil
58
+ end
59
+
60
+ def initialize_keys
61
+ super
62
+ @header_bytes_needed = 12
63
+ end
64
+ end
65
+ end; end; end; end
@@ -0,0 +1,71 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'archive/zip/codec/traditional_encryption/base'
4
+ require 'archive/zip/dos_time'
5
+
6
+ module Archive; class Zip; module Codec; class TraditionalEncryption
7
+ # Archive::Zip::Codec::TraditionalEncryption::Writer is a writable, IO-like
8
+ # object which encrypts data written to it using the traditional encryption
9
+ # algorithm as documented in the ZIP specification and writes the result to
10
+ # a delegate IO object. A _close_ method is also provided which can
11
+ # optionally close the delegate object.
12
+ #
13
+ # Instances of this class should only be accessed via the
14
+ # Archive::Zip::Codec::TraditionalEncryption#compressor method.
15
+ class Writer < Base
16
+ def initialize(delegate, password, mtime, autoclose: true)
17
+ super
18
+
19
+ # A 12 byte header to protect the encrypted file data from attack. The
20
+ # first 10 bytes are random, and the last 2 bytes are the low order word
21
+ # in little endian byte order of the last modified time of the entry in
22
+ # DOS format.
23
+ @header =
24
+ (10.times.map { |_| rand(256) } + [DOSTime.new(@mtime).to_i].pack('V')[0, 2].bytes)
25
+ .map do |byte|
26
+ crypt_char = (byte ^ decrypt_byte).chr
27
+ update_keys(byte.chr)
28
+ crypt_char
29
+ end
30
+ .join
31
+ end
32
+
33
+ # Encrypts and writes _string_ to the delegate IO object. Returns the
34
+ # number of bytes of _string_ written.
35
+ def write(buffer, length: buffer.bytesize)
36
+ result = write_header
37
+ return result if Symbol === result
38
+
39
+ buffer = buffer[0, length] if length < buffer.bytesize
40
+ buffer.to_enum(:each_byte).each_with_index do |byte, idx|
41
+ result = super((byte ^ decrypt_byte).chr)
42
+ if Symbol === result
43
+ return idx if idx > 0
44
+ return result
45
+ end
46
+ update_keys(byte.chr)
47
+ @bytes_processed += 1
48
+ end
49
+
50
+ buffer.bytesize
51
+ end
52
+
53
+ private
54
+
55
+ def write_header
56
+ while @header_idx < @header.size do
57
+ result = delegate.write(@header[@header_idx..-1])
58
+ return result if Symbol === result
59
+
60
+ @header_idx += result
61
+ end
62
+
63
+ nil
64
+ end
65
+
66
+ def initialize_keys
67
+ super
68
+ @header_idx = 0
69
+ end
70
+ end
71
+ end; end; end; end
@@ -1,366 +1,12 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require 'archive/support/integer'
4
- require 'archive/support/io-like'
5
- require 'archive/support/time'
6
- require 'archive/support/zlib'
7
- require 'archive/zip/codec'
3
+ require 'archive/zip/codec/traditional_encryption/reader'
4
+ require 'archive/zip/codec/traditional_encryption/writer'
8
5
 
9
6
  module Archive; class Zip; module Codec
10
7
  # Archive::Zip::Codec::TraditionalEncryption is a handle for the traditional
11
8
  # encryption codec.
12
9
  class TraditionalEncryption
13
- # Archive::Zip::Codec::TraditionalEncryption::Base provides some basic
14
- # methods which are shared between
15
- # Archive::Zip::Codec::TraditionalEncryption::Encrypt and
16
- # Archive::Zip::Codec::TraditionalEncryption::Decrypt.
17
- #
18
- # Do not use this class directly.
19
- class Base
20
- # Creates a new instance of this class. _io_ must be an IO-like object to
21
- # be used as a delegate for IO operations. _password_ should be the
22
- # encryption key. _mtime_ must be the last modified time of the entry to
23
- # be encrypted/decrypted.
24
- def initialize(io, password, mtime)
25
- @io = io
26
- @password = password.nil? ? '' : password
27
- @mtime = mtime
28
- initialize_keys
29
- end
30
-
31
- protected
32
-
33
- # The delegate IO-like object.
34
- attr_reader :io
35
- # The encryption key.
36
- attr_reader :password
37
- # The last modified time of the entry being encrypted. This is used in
38
- # the entryption header as a way to check the password.
39
- attr_reader :mtime
40
-
41
- private
42
-
43
- # Initializes the keys used for encrypting/decrypting data by setting the
44
- # keys to well known values and then processing them with the password.
45
- def initialize_keys
46
- @key0 = 0x12345678
47
- @key1 = 0x23456789
48
- @key2 = 0x34567890
49
- @password.each_byte { |byte| update_keys(byte.chr) }
50
- nil
51
- end
52
-
53
- # Updates the keys following the ZIP specification using _char_, which
54
- # must be a single byte String.
55
- def update_keys(char)
56
- # For some reason not explained in the ZIP specification but discovered
57
- # in the source for InfoZIP, the old CRC value must first have its bits
58
- # flipped before processing. The new CRC value must have its bits
59
- # flipped as well for storage and later use. This applies to the
60
- # handling of @key0 and @key2.
61
- @key0 = ~Zlib.crc32(char, ~@key0)
62
- @key1 = ((@key1 + (@key0 & 0xff)) * 134775813 + 1) & 0xffffffff
63
- @key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
64
- nil
65
- end
66
-
67
- # Returns the next decryption byte based on the current keys.
68
- def decrypt_byte
69
- temp = (@key2 | 2) & 0x0000ffff
70
- ((temp * (temp ^ 1)) >> 8) & 0x000000ff
71
- end
72
- end
73
-
74
- # Archive::Zip::Codec::TraditionalEncryption::Encrypt is a writable, IO-like
75
- # object which encrypts data written to it using the traditional encryption
76
- # algorithm as documented in the ZIP specification and writes the result to
77
- # a delegate IO object. A _close_ method is also provided which can
78
- # optionally close the delegate object.
79
- #
80
- # Instances of this class should only be accessed via the
81
- # Archive::Zip::Codec::TraditionalEncryption#compressor method.
82
- class Encrypt < Base
83
- include IO::Like
84
-
85
- # Creates a new instance of this class with the given argument using #new
86
- # and then passes the instance to the given block. The #close method is
87
- # guaranteed to be called after the block completes.
88
- #
89
- # Equivalent to #new if no block is given.
90
- def self.open(io, password, mtime)
91
- encrypt_io = new(io, password, mtime)
92
- return encrypt_io unless block_given?
93
-
94
- begin
95
- yield(encrypt_io)
96
- ensure
97
- encrypt_io.close unless encrypt_io.closed?
98
- end
99
- end
100
-
101
- # Creates a new instance of this class using _io_ as a data sink. _io_
102
- # must be writable and must provide a write method as IO does or errors
103
- # will be raised when performing write operations. _password_ should be
104
- # the encryption key. _mtime_ must be the last modified time of the entry
105
- # to be encrypted/decrypted.
106
- #
107
- # The _flush_size_ attribute is set to <tt>0</tt> by default under the
108
- # assumption that _io_ is already buffered.
109
- def initialize(io, password, mtime)
110
- # Keep track of the total number of bytes written.
111
- # Set this here so that the call to #initialize_keys caused by the call
112
- # to super below does not cause errors in #unbuffered_write due to this
113
- # attribute being uninitialized.
114
- @total_bytes_in = 0
115
-
116
- # This buffer is used to hold the encrypted version of the string most
117
- # recently sent to #unbuffered_write.
118
- @encrypt_buffer = ''
119
-
120
- super(io, password, mtime)
121
-
122
- # Assume that the delegate IO object is already buffered.
123
- self.flush_size = 0
124
- end
125
-
126
- # Closes the stream after flushing the encryption buffer to the delegate.
127
- # If _close_delegate_ is +true+, the delegate object used as a data sink
128
- # will also be closed using its close method.
129
- #
130
- # Raises IOError if called more than once.
131
- def close(close_delegate = true)
132
- flush()
133
- begin
134
- until @encrypt_buffer.empty? do
135
- @encrypt_buffer.slice!(0, io.write(@encrypt_buffer))
136
- end
137
- rescue Errno::EAGAIN, Errno::EINTR
138
- retry if write_ready?
139
- end
140
-
141
- super()
142
- io.close if close_delegate
143
- nil
144
- end
145
-
146
- private
147
-
148
- # Extend the inherited initialize_keys method to further initialize the
149
- # keys by encrypting and writing a 12 byte header to the delegate IO
150
- # object.
151
- def initialize_keys
152
- super
153
-
154
- # Create and encrypt a 12 byte header to protect the encrypted file data
155
- # from attack. The first 10 bytes are random, and the last 2 bytes are
156
- # the low order word in little endian byte order of the last modified
157
- # time of the entry in DOS format.
158
- header = ''
159
- 10.times do
160
- header << rand(256).chr
161
- end
162
- header << mtime.to_dos_time.pack[0, 2]
163
-
164
- # Take care to ensure that all bytes in the header are written.
165
- while header.size > 0 do
166
- begin
167
- header.slice!(0, unbuffered_write(header))
168
- rescue Errno::EAGAIN, Errno::EINTR
169
- sleep(1)
170
- end
171
- end
172
-
173
- # Reset the total bytes written in order to disregard the header.
174
- @total_bytes_in = 0
175
-
176
- nil
177
- end
178
-
179
- # Allows resetting this object and the delegate object back to the
180
- # beginning of the stream or reporting the current position in the stream.
181
- #
182
- # Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
183
- # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
184
- # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
185
- # method.
186
- def unbuffered_seek(offset, whence = IO::SEEK_SET)
187
- unless offset == 0 &&
188
- ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) ||
189
- whence == IO::SEEK_CUR) then
190
- raise Errno::EINVAL
191
- end
192
-
193
- case whence
194
- when IO::SEEK_SET
195
- io.rewind
196
- @encrypt_buffer = ''
197
- initialize_keys
198
- @total_bytes_in = 0
199
- when IO::SEEK_CUR
200
- @total_bytes_in
201
- end
202
- end
203
-
204
- # Encrypts and writes _string_ to the delegate IO object. Returns the
205
- # number of bytes of _string_ written.
206
- def unbuffered_write(string)
207
- # First try to write out the contents of the encrypt buffer because if
208
- # that raises a failure we can let that pass up the call stack without
209
- # having polluted the encryption state.
210
- until @encrypt_buffer.empty? do
211
- @encrypt_buffer.slice!(0, io.write(@encrypt_buffer))
212
- end
213
- # At this point we can encrypt the given string into a new buffer and
214
- # behave as if it was written.
215
- string.each_byte do |byte|
216
- temp = decrypt_byte
217
- @encrypt_buffer << (byte ^ temp).chr
218
- update_keys(byte.chr)
219
- end
220
- @total_bytes_in += string.length
221
- string.length
222
- end
223
- end
224
-
225
- # Archive::Zip::Codec::TraditionalEncryption::Decrypt is a readable, IO-like
226
- # object which decrypts data data it reads from a delegate IO object using
227
- # the traditional encryption algorithm as documented in the ZIP
228
- # specification. A _close_ method is also provided which can optionally
229
- # close the delegate object.
230
- #
231
- # Instances of this class should only be accessed via the
232
- # Archive::Zip::Codec::TraditionalEncryption#decompressor method.
233
- class Decrypt < Base
234
- include IO::Like
235
-
236
- # Creates a new instance of this class with the given argument using #new
237
- # and then passes the instance to the given block. The #close method is
238
- # guaranteed to be called after the block completes.
239
- #
240
- # Equivalent to #new if no block is given.
241
- def self.open(io, password, mtime)
242
- decrypt_io = new(io, password, mtime)
243
- return decrypt_io unless block_given?
244
-
245
- begin
246
- yield(decrypt_io)
247
- ensure
248
- decrypt_io.close unless decrypt_io.closed?
249
- end
250
- end
251
-
252
- # Creates a new instance of this class using _io_ as a data source. _io_
253
- # must be readable and provide a _read_ method as an IO instance would or
254
- # errors will be raised when performing read operations. _password_
255
- # should be the encryption key. _mtime_ must be the last modified time of
256
- # the entry to be encrypted/decrypted.
257
- #
258
- # This class has extremely limited seek capabilities. It is possible to
259
- # seek with an offset of <tt>0</tt> and a whence of <tt>IO::SEEK_CUR</tt>.
260
- # As a result, the _pos_ and _tell_ methods also work as expected.
261
- #
262
- # Due to certain optimizations within IO::Like#seek and if there is data
263
- # in the read buffer, the _seek_ method can be used to seek forward from
264
- # the current stream position up to the end of the buffer. Unless it is
265
- # known definitively how much data is in the buffer, it is best to avoid
266
- # relying on this behavior.
267
- #
268
- # If _io_ also responds to _rewind_, then the _rewind_ method of this
269
- # class can be used to reset the whole stream back to the beginning. Using
270
- # _seek_ of this class to seek directly to offset <tt>0</tt> using
271
- # <tt>IO::SEEK_SET</tt> for whence will also work in this case.
272
- #
273
- # Any other seeking attempts, will raise Errno::EINVAL exceptions.
274
- #
275
- # The _fill_size_ attribute is set to <tt>0</tt> by default under the
276
- # assumption that _io_ is already buffered.
277
- def initialize(io, password, mtime)
278
- # Keep track of the total number of bytes read.
279
- # Set this here so that the call to #initialize_keys caused by the call
280
- # to super below does not cause errors in #unbuffered_read due to this
281
- # attribute being uninitialized.
282
- @total_bytes_out = 0
283
-
284
- super(io, password, mtime)
285
-
286
- # Assume that the delegate IO object is already buffered.
287
- self.fill_size = 0
288
- end
289
-
290
- # Closes this object so that further write operations will fail. If
291
- # _close_delegate_ is +true+, the delegate object used as a data source
292
- # will also be closed using its close method.
293
- def close(close_delegate = true)
294
- super()
295
- io.close if close_delegate
296
- end
297
-
298
- private
299
-
300
- # Extend the inherited initialize_keys method to further initialize the
301
- # keys by encrypting and writing a 12 byte header to the delegate IO
302
- # object.
303
- def initialize_keys
304
- super
305
-
306
- # Load the 12 byte header taking care to ensure that all bytes are read.
307
- bytes_needed = 12
308
- while bytes_needed > 0 do
309
- begin
310
- bytes_read = unbuffered_read(bytes_needed)
311
- bytes_needed -= bytes_read.size
312
- rescue Errno::EAGAIN, Errno::EINTR
313
- sleep(1)
314
- end
315
- end
316
-
317
- # Reset the total bytes read in order to disregard the header.
318
- @total_bytes_out = 0
319
-
320
- nil
321
- end
322
-
323
- # Reads, decrypts, and returns at most _length_ bytes from the delegate IO
324
- # object.
325
- #
326
- # Raises EOFError if there is no data to read.
327
- def unbuffered_read(length)
328
- buffer = io.read(length)
329
- raise EOFError, 'end of file reached' if buffer.nil?
330
- @total_bytes_out += buffer.length
331
-
332
- 0.upto(buffer.length - 1) do |i|
333
- buffer[i] = (buffer[i].ord ^ decrypt_byte).chr
334
- update_keys(buffer[i].chr)
335
- end
336
- buffer
337
- end
338
-
339
- # Allows resetting this object and the delegate object back to the
340
- # beginning of the stream or reporting the current position in the stream.
341
- #
342
- # Raises Errno::EINVAL unless _offset_ is <tt>0</tt> and _whence_ is
343
- # either IO::SEEK_SET or IO::SEEK_CUR. Raises Errno::EINVAL if _whence_
344
- # is IO::SEEK_SEK and the delegate object does not respond to the _rewind_
345
- # method.
346
- def unbuffered_seek(offset, whence = IO::SEEK_SET)
347
- unless offset == 0 &&
348
- ((whence == IO::SEEK_SET && @io.respond_to?(:rewind)) ||
349
- whence == IO::SEEK_CUR) then
350
- raise Errno::EINVAL
351
- end
352
-
353
- case whence
354
- when IO::SEEK_SET
355
- io.rewind
356
- initialize_keys
357
- @total_bytes_out = 0
358
- when IO::SEEK_CUR
359
- @total_bytes_out
360
- end
361
- end
362
- end
363
-
364
10
  # The last modified time of the entry to be processed. Set this before
365
11
  # calling #encryptor or #decryptor.
366
12
  attr_accessor :mtime
@@ -369,20 +15,20 @@ module Archive; class Zip; module Codec
369
15
  # Archive::Zip::Entry for encryption codec objects.
370
16
  #
371
17
  # A convenience method for creating an
372
- # Archive::Zip::Codec::TraditionalEncryption::Encrypt object using that
18
+ # Archive::Zip::Codec::TraditionalEncryption::Writer object using that
373
19
  # class' open method.
374
20
  def encryptor(io, password, &b)
375
- Encrypt.open(io, password, mtime, &b)
21
+ Writer.open(io, password, mtime, &b)
376
22
  end
377
23
 
378
24
  # This method signature is part of the interface contract expected by
379
25
  # Archive::Zip::Entry for encryption codec objects.
380
26
  #
381
27
  # A convenience method for creating an
382
- # Archive::Zip::Codec::TraditionalEncryption::Decrypt object using that
28
+ # Archive::Zip::Codec::TraditionalEncryption::Reader object using that
383
29
  # class' open method.
384
30
  def decryptor(io, password, &b)
385
- Decrypt.open(io, password, mtime, &b)
31
+ Reader.open(io, password, mtime, &b)
386
32
  end
387
33
 
388
34
  # This method signature is part of the interface contract expected by