/
traditional_encryption.rb
99 lines (80 loc) · 1.97 KB
/
traditional_encryption.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
module Zip
module TraditionalEncryption
def initialize(password)
@password = password
reset_keys!
end
def header_bytesize
12
end
def gp_flags
0x0001 | 0x0008
end
protected
def reset_keys!
@key0 = 0x12345678
@key1 = 0x23456789
@key2 = 0x34567890
@password.each_byte do |byte|
update_keys(byte.chr)
end
end
def update_keys(num)
@key0 = ~Zlib.crc32(num, ~@key0)
@key1 = ((@key1 + (@key0 & 0xff)) * 134_775_813 + 1) & 0xffffffff
@key2 = ~Zlib.crc32((@key1 >> 24).chr, ~@key2)
end
def decrypt_byte
temp = (@key2 & 0xffff) | 2
((temp * (temp ^ 1)) >> 8) & 0xff
end
end
class TraditionalEncrypter < Encrypter
include TraditionalEncryption
def header(mtime)
[].tap do |header|
(header_bytesize - 2).times do
header << Random.rand(0..255)
end
header << (mtime.to_binary_dos_time & 0xff)
header << (mtime.to_binary_dos_time >> 8)
end.map { |x| encode x }.pack('C*')
end
def encrypt(data)
data.unpack('C*').map { |x| encode x }.pack('C*')
end
def data_descriptor(crc32, compressed_size, uncomprssed_size)
[0x08074b50, crc32, compressed_size, uncomprssed_size].pack('VVVV')
end
def reset!
reset_keys!
end
private
def encode(num)
t = decrypt_byte
update_keys(num.chr)
t ^ num
end
end
class TraditionalDecrypter < Decrypter
include TraditionalEncryption
def decrypt(data)
data.unpack('C*').map { |x| decode x }.pack('C*')
end
def reset!(header)
reset_keys!
header.each_byte do |x|
decode x
end
end
private
def decode(num)
num ^= decrypt_byte
update_keys(num.chr)
num
end
end
end
# Copyright (C) 2002, 2003 Thomas Sondergaard
# rubyzip is free software; you can redistribute it and/or
# modify it under the terms of the ruby license.