-
Notifications
You must be signed in to change notification settings - Fork 313
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
209 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
require 'openssl' | ||
require 'stringio' | ||
|
||
module Zip | ||
class Decrypter < Decompressor #:nodoc:all | ||
VERIFIER_LENGTH = 2 | ||
BLOCK_SIZE = 16 | ||
AUTHENTICATION_CODE_LENGTH = 10 | ||
|
||
attr_writer :password | ||
|
||
def initialize(input_stream, encryption_strength, entry_size, decompressor) | ||
super(input_stream) | ||
|
||
@data_length = entry_size - AUTHENTICATION_CODE_LENGTH | ||
@decompressor = decompressor | ||
@decompressor.input_stream = StringIO.new | ||
@encryption_strength = encryption_strength | ||
@prepared = false | ||
end | ||
|
||
def sysread(number_of_bytes = nil, buf = '') | ||
prepare_aes unless @prepared | ||
|
||
amount_to_read = @data_length | ||
raise RuntimeError, "Incorrect entry size given, can't proceed" if amount_to_read <= 0 | ||
|
||
counter = 1 | ||
while amount_to_read > 0 | ||
set_iv(counter) | ||
|
||
encrypted = @input_stream.read([BLOCK_SIZE, amount_to_read].min) | ||
# Add the decrypted data to the IO object the decompressor interacts with | ||
@decompressor.input_stream.write(@cipher.update(encrypted)) | ||
|
||
amount_to_read -= BLOCK_SIZE | ||
counter += 1 | ||
end | ||
|
||
# TODO: Check Authentication value | ||
@input_stream.read(AUTHENTICATION_CODE_LENGTH) | ||
|
||
@decompressor.input_stream.rewind | ||
@decompressor.sysread | ||
end | ||
|
||
def input_finished? | ||
@decompressor.input_finished? | ||
end | ||
|
||
alias :eof :input_finished? | ||
alias :eof? :input_finished? | ||
|
||
private | ||
|
||
def prepare_aes | ||
raise RuntimeError, "No password given" if @password.nil? | ||
n = @encryption_strength + 1 | ||
|
||
headers = { | ||
bits: 64 * n, | ||
key_length: 8 * n, | ||
mac_length: 8 * n, | ||
salt_length: 4 * n | ||
} | ||
|
||
raise RuntimeError, "AES-#{headers[:bits]} is not supported." unless [0x01, 0x02, 0x03].include? @encryption_strength | ||
|
||
@cipher = OpenSSL::Cipher::AES.new(headers[:bits], :CTR) | ||
@cipher.decrypt | ||
|
||
salt = @input_stream.read(headers[:salt_length]) | ||
verification = @input_stream.read(VERIFIER_LENGTH) | ||
# The first few bytes are AES setup. Ensure we don't read beyond the end of the data during sysread | ||
@data_length -= (headers[:salt_length] + VERIFIER_LENGTH) | ||
|
||
key = OpenSSL::PKCS5.pbkdf2_hmac_sha1( | ||
@password, | ||
salt, | ||
1000, | ||
headers[:key_length] + headers[:mac_length] + VERIFIER_LENGTH | ||
) | ||
|
||
raise RuntimeError, "Incorrect password" unless key[-2..-1] == verification | ||
@cipher.key = key | ||
|
||
@prepared = true | ||
end | ||
|
||
def set_iv(counter) | ||
# Reverse engineered this value from Zip4j's AES support. | ||
@cipher.iv = [counter].pack("Vx12") | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
module Zip | ||
# Info-ZIP Extra for AES encryption | ||
class ExtraField::AES < ExtraField::Generic | ||
attr_reader :data_size, :vendor_version, :vendor_id, :encryption_strength, :compression_method | ||
HEADER_ID = "\x01\x99".force_encoding("ASCII-8BIT") | ||
register_map | ||
|
||
def initialize(binstr = nil) | ||
@data_size = nil | ||
@vendor_version = nil | ||
@vendor_id = nil | ||
@encryption_strength = nil | ||
@compression_method = nil | ||
binstr and merge(binstr) | ||
end | ||
|
||
def merge(binstr) | ||
return if binstr.empty? | ||
_, @data_size, @vendor_version, @vendor_id, @encryption_strength, @compression_method = binstr.to_s.unpack("vvva2Cv") | ||
end | ||
|
||
def pack_for_local | ||
return '' unless @data_size && @vendor_version && @vendor_id && @encryption_strength && @compression_method | ||
[0x01, 0x99, @data_size, @vendor_version, @vendor_id, @encryption_strength, @compression_method].pack("vvvvM2Cv") | ||
end | ||
|
||
def pack_for_c_dir | ||
pack_for_local | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters