origami 1.2.7 → 2.0.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +66 -0
- data/README.md +112 -0
- data/bin/config/pdfcop.conf.yml +232 -233
- data/bin/gui/about.rb +27 -37
- data/bin/gui/config.rb +108 -117
- data/bin/gui/file.rb +416 -365
- data/bin/gui/gtkhex.rb +1138 -1153
- data/bin/gui/hexview.rb +55 -57
- data/bin/gui/imgview.rb +48 -51
- data/bin/gui/menu.rb +388 -386
- data/bin/gui/properties.rb +114 -130
- data/bin/gui/signing.rb +571 -617
- data/bin/gui/textview.rb +77 -95
- data/bin/gui/treeview.rb +382 -387
- data/bin/gui/walker.rb +227 -232
- data/bin/gui/xrefs.rb +56 -60
- data/bin/pdf2pdfa +53 -57
- data/bin/pdf2ruby +212 -228
- data/bin/pdfcop +338 -348
- data/bin/pdfdecompress +58 -65
- data/bin/pdfdecrypt +56 -60
- data/bin/pdfencrypt +75 -80
- data/bin/pdfexplode +185 -182
- data/bin/pdfextract +201 -218
- data/bin/pdfmetadata +83 -82
- data/bin/pdfsh +4 -5
- data/bin/pdfwalker +1 -2
- data/bin/shell/.irbrc +45 -82
- data/bin/shell/console.rb +105 -130
- data/bin/shell/hexdump.rb +40 -64
- data/examples/README.md +34 -0
- data/examples/attachments/attachment.rb +38 -0
- data/examples/attachments/nested_document.rb +51 -0
- data/examples/encryption/encryption.rb +28 -0
- data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
- data/examples/flash/flash.rb +37 -0
- data/{samples → examples}/flash/helloworld.swf +0 -0
- data/examples/forms/javascript.rb +54 -0
- data/examples/forms/xfa.rb +115 -0
- data/examples/javascript/hello_world.rb +22 -0
- data/examples/javascript/js_emulation.rb +54 -0
- data/examples/loop/goto.rb +32 -0
- data/examples/loop/named.rb +33 -0
- data/examples/signature/signature.rb +65 -0
- data/examples/uri/javascript.rb +56 -0
- data/examples/uri/open-uri.rb +21 -0
- data/examples/uri/submitform.rb +47 -0
- data/lib/origami.rb +29 -42
- data/lib/origami/3d.rb +350 -225
- data/lib/origami/acroform.rb +262 -288
- data/lib/origami/actions.rb +268 -288
- data/lib/origami/annotations.rb +697 -722
- data/lib/origami/array.rb +258 -184
- data/lib/origami/boolean.rb +74 -84
- data/lib/origami/catalog.rb +397 -434
- data/lib/origami/collections.rb +144 -0
- data/lib/origami/destinations.rb +233 -194
- data/lib/origami/dictionary.rb +253 -232
- data/lib/origami/encryption.rb +1274 -1243
- data/lib/origami/export.rb +232 -268
- data/lib/origami/extensions/fdf.rb +307 -220
- data/lib/origami/extensions/ppklite.rb +368 -435
- data/lib/origami/filespec.rb +197 -0
- data/lib/origami/filters.rb +301 -295
- data/lib/origami/filters/ascii.rb +177 -180
- data/lib/origami/filters/ccitt.rb +528 -535
- data/lib/origami/filters/crypt.rb +26 -35
- data/lib/origami/filters/dct.rb +46 -52
- data/lib/origami/filters/flate.rb +95 -94
- data/lib/origami/filters/jbig2.rb +49 -55
- data/lib/origami/filters/jpx.rb +38 -44
- data/lib/origami/filters/lzw.rb +189 -183
- data/lib/origami/filters/predictors.rb +221 -235
- data/lib/origami/filters/runlength.rb +103 -104
- data/lib/origami/font.rb +173 -186
- data/lib/origami/functions.rb +67 -81
- data/lib/origami/graphics.rb +25 -21
- data/lib/origami/graphics/colors.rb +178 -187
- data/lib/origami/graphics/instruction.rb +79 -85
- data/lib/origami/graphics/path.rb +142 -148
- data/lib/origami/graphics/patterns.rb +160 -167
- data/lib/origami/graphics/render.rb +43 -50
- data/lib/origami/graphics/state.rb +138 -153
- data/lib/origami/graphics/text.rb +188 -205
- data/lib/origami/graphics/xobject.rb +819 -815
- data/lib/origami/header.rb +63 -78
- data/lib/origami/javascript.rb +596 -597
- data/lib/origami/linearization.rb +285 -290
- data/lib/origami/metadata.rb +139 -148
- data/lib/origami/name.rb +112 -148
- data/lib/origami/null.rb +53 -62
- data/lib/origami/numeric.rb +162 -175
- data/lib/origami/obfuscation.rb +186 -174
- data/lib/origami/object.rb +593 -573
- data/lib/origami/outline.rb +42 -47
- data/lib/origami/outputintents.rb +73 -82
- data/lib/origami/page.rb +703 -592
- data/lib/origami/parser.rb +238 -290
- data/lib/origami/parsers/fdf.rb +41 -33
- data/lib/origami/parsers/pdf.rb +75 -95
- data/lib/origami/parsers/pdf/lazy.rb +137 -0
- data/lib/origami/parsers/pdf/linear.rb +64 -66
- data/lib/origami/parsers/ppklite.rb +34 -70
- data/lib/origami/pdf.rb +1030 -1005
- data/lib/origami/reference.rb +102 -102
- data/lib/origami/signature.rb +591 -609
- data/lib/origami/stream.rb +668 -551
- data/lib/origami/string.rb +397 -373
- data/lib/origami/template/patterns.rb +56 -0
- data/lib/origami/template/widgets.rb +151 -0
- data/lib/origami/trailer.rb +144 -158
- data/lib/origami/tree.rb +62 -0
- data/lib/origami/version.rb +23 -0
- data/lib/origami/webcapture.rb +88 -79
- data/lib/origami/xfa.rb +2863 -2882
- data/lib/origami/xreftable.rb +472 -384
- data/test/dataset/calc.pdf +85 -0
- data/test/dataset/crypto.pdf +82 -0
- data/test/dataset/empty.pdf +49 -0
- data/test/test_actions.rb +27 -0
- data/test/test_annotations.rb +90 -0
- data/test/test_pages.rb +31 -0
- data/test/test_pdf.rb +16 -0
- data/test/test_pdf_attachment.rb +34 -0
- data/test/test_pdf_create.rb +24 -0
- data/test/test_pdf_encrypt.rb +95 -0
- data/test/test_pdf_parse.rb +96 -0
- data/test/test_pdf_sign.rb +58 -0
- data/test/test_streams.rb +182 -0
- data/test/test_xrefs.rb +67 -0
- metadata +88 -58
- data/README +0 -67
- data/bin/pdf2graph +0 -121
- data/bin/pdfcocoon +0 -104
- data/lib/origami/file.rb +0 -233
- data/samples/README.txt +0 -45
- data/samples/actions/launch/calc.rb +0 -87
- data/samples/actions/launch/winparams.rb +0 -22
- data/samples/actions/loop/loopgoto.rb +0 -24
- data/samples/actions/loop/loopnamed.rb +0 -21
- data/samples/actions/named/named.rb +0 -31
- data/samples/actions/samba/smbrelay.rb +0 -26
- data/samples/actions/webbug/submitform.js +0 -26
- data/samples/actions/webbug/webbug-browser.rb +0 -68
- data/samples/actions/webbug/webbug-js.rb +0 -67
- data/samples/actions/webbug/webbug-reader.rb +0 -90
- data/samples/attachments/attach.rb +0 -40
- data/samples/attachments/attached.txt +0 -1
- data/samples/crypto/crypto.rb +0 -28
- data/samples/digsig/signed.rb +0 -46
- data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
- data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
- data/samples/exploits/exploit_customdictopen.rb +0 -55
- data/samples/exploits/getannots.rb +0 -69
- data/samples/flash/flash.rb +0 -31
- data/samples/javascript/attached.txt +0 -1
- data/samples/javascript/js.rb +0 -52
- data/templates/patterns.rb +0 -66
- data/templates/widgets.rb +0 -173
- data/templates/xdp.rb +0 -92
- data/test/ts_pdf.rb +0 -50
data/lib/origami/encryption.rb
CHANGED
|
@@ -1,1404 +1,1435 @@
|
|
|
1
1
|
=begin
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19
|
-
GNU Lesser General Public License for more details.
|
|
20
|
-
|
|
21
|
-
You should have received a copy of the GNU Lesser General Public License
|
|
22
|
-
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
|
3
|
+
This file is part of Origami, PDF manipulation framework for Ruby
|
|
4
|
+
Copyright (C) 2016 Guillaume Delugré.
|
|
5
|
+
|
|
6
|
+
Origami is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Lesser General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
Origami is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Lesser General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
17
|
+
along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
|
23
18
|
|
|
24
19
|
=end
|
|
25
20
|
|
|
26
21
|
begin
|
|
27
|
-
|
|
22
|
+
require 'openssl' if Origami::OPTIONS[:use_openssl]
|
|
28
23
|
rescue LoadError
|
|
29
|
-
|
|
24
|
+
Origami::OPTIONS[:use_openssl] = false
|
|
30
25
|
end
|
|
31
26
|
|
|
27
|
+
require 'securerandom'
|
|
32
28
|
require 'digest/md5'
|
|
33
29
|
require 'digest/sha2'
|
|
34
30
|
|
|
35
31
|
module Origami
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class EncryptionInvalidPasswordError < EncryptionError #:nodoc:
|
|
41
|
-
end
|
|
33
|
+
class EncryptionError < Error #:nodoc:
|
|
34
|
+
end
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
class EncryptionInvalidPasswordError < EncryptionError #:nodoc:
|
|
37
|
+
end
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
#
|
|
49
|
-
# Returns whether the PDF file is encrypted.
|
|
50
|
-
#
|
|
51
|
-
def is_encrypted?
|
|
52
|
-
has_attr? :Encrypt
|
|
39
|
+
class EncryptionNotSupportedError < EncryptionError #:nodoc:
|
|
53
40
|
end
|
|
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
|
-
|
|
41
|
+
|
|
42
|
+
class PDF
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# Returns whether the PDF file is encrypted.
|
|
46
|
+
#
|
|
47
|
+
def encrypted?
|
|
48
|
+
trailer_key? :Encrypt
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
#
|
|
52
|
+
# Decrypts the current document (only RC4 40..128 bits).
|
|
53
|
+
# _passwd_:: The password to decrypt the document.
|
|
54
|
+
#
|
|
55
|
+
def decrypt(passwd = "")
|
|
56
|
+
raise EncryptionError, "PDF is not encrypted" unless self.encrypted?
|
|
57
|
+
|
|
58
|
+
encrypt_dict = trailer_key(:Encrypt)
|
|
59
|
+
handler = Encryption::Standard::Dictionary.new(encrypt_dict.dup)
|
|
60
|
+
|
|
61
|
+
unless handler.Filter == :Standard
|
|
62
|
+
raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter.to_s}'"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
crypt_filters = {
|
|
66
|
+
Identity: Encryption::Identity
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
case handler.V.to_i
|
|
70
|
+
when 1,2
|
|
71
|
+
crypt_filters = Hash.new(Encryption::RC4)
|
|
72
|
+
string_filter = stream_filter = nil
|
|
73
|
+
when 4,5
|
|
74
|
+
crypt_filters = {
|
|
75
|
+
Identity: Encryption::Identity
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if handler[:CF].is_a?(Dictionary)
|
|
79
|
+
handler[:CF].each_pair do |name, cf|
|
|
80
|
+
next unless cf.is_a?(Dictionary)
|
|
81
|
+
|
|
82
|
+
crypt_filters[name.value] =
|
|
83
|
+
if cf[:CFM] == :V2 then Encryption::RC4
|
|
84
|
+
elsif cf[:CFM] == :AESV2 then Encryption::AES
|
|
85
|
+
elsif cf[:CFM] == :None then Encryption::Identity
|
|
86
|
+
elsif cf[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
|
|
87
|
+
else
|
|
88
|
+
raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
string_filter = handler.StrF.is_a?(Name) ? handler.StrF.value : :Identity
|
|
94
|
+
stream_filter = handler.StmF.is_a?(Name) ? handler.StmF.value : :Identity
|
|
95
|
+
|
|
96
|
+
unless crypt_filters.key?(string_filter)
|
|
97
|
+
raise EncryptionError, "Invalid StrF value in encryption dictionary"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
unless crypt_filters.key?(stream_filter)
|
|
101
|
+
raise EncryptionError, "Invalid StmF value in encryption dictionary"
|
|
88
102
|
end
|
|
89
103
|
else
|
|
90
|
-
|
|
104
|
+
raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
|
|
91
105
|
end
|
|
92
106
|
|
|
93
|
-
|
|
94
|
-
|
|
107
|
+
doc_id = trailer_key(:ID)
|
|
108
|
+
unless doc_id.is_a?(Array)
|
|
109
|
+
raise EncryptionError, "Document ID was not found or is invalid" unless handler.V.to_i == 5
|
|
110
|
+
else
|
|
111
|
+
doc_id = doc_id.first
|
|
112
|
+
end
|
|
95
113
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
114
|
+
if handler.is_user_password?(passwd, doc_id)
|
|
115
|
+
encryption_key = handler.compute_user_encryption_key(passwd, doc_id)
|
|
116
|
+
elsif handler.is_owner_password?(passwd, doc_id)
|
|
117
|
+
if handler.V.to_i < 5
|
|
118
|
+
user_passwd = handler.retrieve_user_password(passwd)
|
|
119
|
+
encryption_key = handler.compute_user_encryption_key(user_passwd, doc_id)
|
|
101
120
|
else
|
|
102
|
-
|
|
121
|
+
encryption_key = handler.compute_owner_encryption_key(passwd)
|
|
103
122
|
end
|
|
104
123
|
else
|
|
105
|
-
|
|
124
|
+
raise EncryptionInvalidPasswordError
|
|
106
125
|
end
|
|
107
126
|
|
|
108
|
-
|
|
109
|
-
str_algo = stm_algo = Encryption::Identity
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
else
|
|
113
|
-
raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
doc_id = get_doc_attr(:ID)
|
|
117
|
-
unless doc_id.is_a?(Array)
|
|
118
|
-
raise EncryptionError, "Document ID was not found or is invalid" unless handler.V.to_i == 5
|
|
119
|
-
else
|
|
120
|
-
doc_id = doc_id.first
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
if handler.is_user_password?(passwd, doc_id)
|
|
124
|
-
encryption_key = handler.compute_user_encryption_key(passwd, doc_id)
|
|
125
|
-
elsif handler.is_owner_password?(passwd, doc_id)
|
|
126
|
-
if handler.V.to_i < 5
|
|
127
|
-
user_passwd = handler.retrieve_user_password(passwd)
|
|
128
|
-
encryption_key = handler.compute_user_encryption_key(user_passwd, doc_id)
|
|
129
|
-
else
|
|
130
|
-
encryption_key = handler.compute_owner_encryption_key(passwd)
|
|
131
|
-
end
|
|
132
|
-
else
|
|
133
|
-
raise EncryptionInvalidPasswordError
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
#self.extend(Encryption::EncryptedDocument)
|
|
138
|
-
#self.encryption_dict = encrypt_dict
|
|
139
|
-
#self.encryption_key = encryption_key
|
|
140
|
-
#self.stm_algo = self.str_algo = algorithm
|
|
141
|
-
|
|
142
|
-
encrypt_metadata = (handler.EncryptMetadata != false)
|
|
143
|
-
|
|
144
|
-
self.extend(Encryption::EncryptedDocument)
|
|
145
|
-
self.encryption_dict = handler
|
|
146
|
-
self.encryption_key = encryption_key
|
|
147
|
-
self.stm_algo,self.str_algo = stm_algo,str_algo
|
|
148
|
-
|
|
149
|
-
#
|
|
150
|
-
# Should be fixed to exclude only the active XRefStream
|
|
151
|
-
#
|
|
152
|
-
metadata = self.Catalog.Metadata
|
|
153
|
-
|
|
154
|
-
self.indirect_objects.each do |indobj|
|
|
155
|
-
encrypted_objects = []
|
|
156
|
-
case indobj
|
|
157
|
-
when String,Stream then encrypted_objects << indobj
|
|
158
|
-
when Dictionary,Array then encrypted_objects |= indobj.strings_cache
|
|
159
|
-
end
|
|
127
|
+
encrypt_metadata = (handler.EncryptMetadata != false)
|
|
160
128
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
obj.equal?(encrypt_dict[:O]) or
|
|
167
|
-
obj.equal?(encrypt_dict[:UE]) or
|
|
168
|
-
obj.equal?(encrypt_dict[:OE]) or
|
|
169
|
-
obj.equal?(encrypt_dict[:Perms]) or
|
|
170
|
-
(obj.parent.is_a?(Signature::DigitalSignature) and obj.equal?(obj.parent[:Contents]))
|
|
171
|
-
|
|
172
|
-
obj.extend(Encryption::EncryptedString) unless obj.is_a?(Encryption::EncryptedString)
|
|
173
|
-
obj.encryption_handler = handler
|
|
174
|
-
obj.encryption_key = encryption_key
|
|
175
|
-
obj.algorithm = str_algo
|
|
176
|
-
obj.decrypt!
|
|
177
|
-
|
|
178
|
-
when Stream
|
|
179
|
-
next if obj.is_a?(XRefStream) or (not encrypt_metadata and obj.equal?(metadata))
|
|
180
|
-
obj.extend(Encryption::EncryptedStream) unless obj.is_a?(Encryption::EncryptedStream)
|
|
181
|
-
obj.encryption_handler = handler
|
|
182
|
-
obj.encryption_key = encryption_key
|
|
183
|
-
obj.algorithm = stm_algo
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
129
|
+
self.extend(Encryption::EncryptedDocument)
|
|
130
|
+
self.encryption_handler = handler
|
|
131
|
+
self.crypt_filters = crypt_filters
|
|
132
|
+
self.encryption_key = encryption_key
|
|
133
|
+
self.stm_filter, self.str_filter = stream_filter, string_filter
|
|
187
134
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
case params[:cipher].upcase
|
|
221
|
-
when 'RC4'
|
|
222
|
-
algorithm = Encryption::ARC4
|
|
223
|
-
if (40..128) === params[:key_size] and params[:key_size] % 8 == 0
|
|
224
|
-
if params[:key_size] > 40
|
|
225
|
-
version = 2
|
|
226
|
-
revision = 3
|
|
227
|
-
else
|
|
228
|
-
version = 1
|
|
229
|
-
revision = 2
|
|
135
|
+
#
|
|
136
|
+
# Should be fixed to exclude only the active XRefStream
|
|
137
|
+
#
|
|
138
|
+
metadata = self.Catalog.Metadata
|
|
139
|
+
|
|
140
|
+
self.indirect_objects.each do |indobj|
|
|
141
|
+
encrypted_objects = []
|
|
142
|
+
case indobj
|
|
143
|
+
when String,Stream then encrypted_objects << indobj
|
|
144
|
+
when Dictionary,Array then encrypted_objects |= indobj.strings_cache
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
encrypted_objects.each do |obj|
|
|
148
|
+
case obj
|
|
149
|
+
when String
|
|
150
|
+
next if obj.equal?(encrypt_dict[:U]) or
|
|
151
|
+
obj.equal?(encrypt_dict[:O]) or
|
|
152
|
+
obj.equal?(encrypt_dict[:UE]) or
|
|
153
|
+
obj.equal?(encrypt_dict[:OE]) or
|
|
154
|
+
obj.equal?(encrypt_dict[:Perms]) or
|
|
155
|
+
(obj.parent.is_a?(Signature::DigitalSignature) and
|
|
156
|
+
obj.equal?(obj.parent[:Contents]))
|
|
157
|
+
|
|
158
|
+
obj.extend(Encryption::EncryptedString) unless obj.is_a?(Encryption::EncryptedString)
|
|
159
|
+
obj.decrypt!
|
|
160
|
+
|
|
161
|
+
when Stream
|
|
162
|
+
next if obj.is_a?(XRefStream) or (not encrypt_metadata and obj.equal?(metadata))
|
|
163
|
+
|
|
164
|
+
obj.extend(Encryption::EncryptedStream) unless obj.is_a?(Encryption::EncryptedStream)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
230
167
|
end
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
168
|
+
|
|
169
|
+
self
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
#
|
|
173
|
+
# Encrypts the current document with the provided passwords.
|
|
174
|
+
# The document will be encrypted at writing-on-disk time.
|
|
175
|
+
# _userpasswd_:: The user password.
|
|
176
|
+
# _ownerpasswd_:: The owner password.
|
|
177
|
+
# _options_:: A set of options to configure encryption.
|
|
178
|
+
#
|
|
179
|
+
def encrypt(options = {})
|
|
180
|
+
raise EncryptionError, "PDF is already encrypted" if self.encrypted?
|
|
181
|
+
|
|
182
|
+
#
|
|
183
|
+
# Default encryption options.
|
|
184
|
+
#
|
|
185
|
+
params =
|
|
186
|
+
{
|
|
187
|
+
:user_passwd => '',
|
|
188
|
+
:owner_passwd => '',
|
|
189
|
+
:cipher => 'rc4', # :RC4 or :AES
|
|
190
|
+
:key_size => 128, # Key size in bits
|
|
191
|
+
:hardened => false, # Use newer password validation (since Reader X)
|
|
192
|
+
:encrypt_metadata => true, # Metadata shall be encrypted?
|
|
193
|
+
:permissions => Encryption::Standard::Permissions::ALL # Document permissions
|
|
194
|
+
}.update(options)
|
|
195
|
+
|
|
196
|
+
userpasswd, ownerpasswd = params[:user_passwd], params[:owner_passwd]
|
|
197
|
+
|
|
198
|
+
case params[:cipher].upcase
|
|
199
|
+
when 'RC4'
|
|
200
|
+
algorithm = Encryption::RC4
|
|
201
|
+
if (40..128) === params[:key_size] and params[:key_size] % 8 == 0
|
|
202
|
+
if params[:key_size] > 40
|
|
203
|
+
version = 2
|
|
204
|
+
revision = 3
|
|
205
|
+
else
|
|
206
|
+
version = 1
|
|
207
|
+
revision = 2
|
|
208
|
+
end
|
|
209
|
+
else
|
|
210
|
+
raise EncryptionError, "Invalid RC4 key length"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
crypt_filters = Hash.new(algorithm)
|
|
214
|
+
string_filter = stream_filter = nil
|
|
215
|
+
|
|
216
|
+
when 'AES'
|
|
217
|
+
algorithm = Encryption::AES
|
|
218
|
+
if params[:key_size] == 128
|
|
219
|
+
version = revision = 4
|
|
220
|
+
elsif params[:key_size] == 256
|
|
221
|
+
version = 5
|
|
222
|
+
if params[:hardened]
|
|
223
|
+
revision = 6
|
|
224
|
+
else
|
|
225
|
+
revision = 5
|
|
226
|
+
end
|
|
227
|
+
else
|
|
228
|
+
raise EncryptionError, "Invalid AES key length (Only 128 and 256 bits keys are supported)"
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
crypt_filters = {
|
|
232
|
+
Identity: Encryption::Identity,
|
|
233
|
+
StdCF: algorithm
|
|
234
|
+
}
|
|
235
|
+
string_filter = stream_filter = :StdCF
|
|
236
|
+
|
|
242
237
|
else
|
|
243
|
-
|
|
238
|
+
raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
|
|
244
239
|
end
|
|
245
|
-
else
|
|
246
|
-
raise EncryptionError, "Invalid AES key length (Only 128 and 256 bits keys are supported)"
|
|
247
|
-
end
|
|
248
|
-
else
|
|
249
|
-
raise EncryptionNotSupportedError, "Cipher not supported : #{params[:cipher]}"
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
doc_id = (get_doc_attr(:ID) || gen_id).first
|
|
253
|
-
|
|
254
|
-
handler = Encryption::Standard::Dictionary.new
|
|
255
|
-
handler.Filter = :Standard #:nodoc:
|
|
256
|
-
handler.V = version
|
|
257
|
-
handler.R = revision
|
|
258
|
-
handler.Length = params[:key_size]
|
|
259
|
-
handler.P = -1 # params[:Permissions]
|
|
260
|
-
|
|
261
|
-
if revision >= 4
|
|
262
|
-
handler.EncryptMetadata = params[:encrypt_metadata]
|
|
263
|
-
handler.CF = Dictionary.new
|
|
264
|
-
cryptfilter = Encryption::CryptFilterDictionary.new
|
|
265
|
-
cryptfilter.AuthEvent = :DocOpen
|
|
266
|
-
|
|
267
|
-
if revision == 4
|
|
268
|
-
cryptfilter.CFM = :AESV2
|
|
269
|
-
else
|
|
270
|
-
cryptfilter.CFM = :AESV3
|
|
271
|
-
end
|
|
272
240
|
|
|
273
|
-
|
|
241
|
+
doc_id = (trailer_key(:ID) || generate_id).first
|
|
274
242
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
243
|
+
handler = Encryption::Standard::Dictionary.new
|
|
244
|
+
handler.Filter = :Standard #:nodoc:
|
|
245
|
+
handler.V = version
|
|
246
|
+
handler.R = revision
|
|
247
|
+
handler.Length = params[:key_size]
|
|
248
|
+
handler.P = -1 # params[:Permissions]
|
|
281
249
|
|
|
282
|
-
|
|
283
|
-
|
|
250
|
+
if revision >= 4
|
|
251
|
+
handler.EncryptMetadata = params[:encrypt_metadata]
|
|
252
|
+
handler.CF = Dictionary.new
|
|
253
|
+
cryptfilter = Encryption::CryptFilterDictionary.new
|
|
254
|
+
cryptfilter.AuthEvent = :DocOpen
|
|
284
255
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
256
|
+
if revision == 4
|
|
257
|
+
cryptfilter.CFM = :AESV2
|
|
258
|
+
else
|
|
259
|
+
cryptfilter.CFM = :AESV3
|
|
260
|
+
end
|
|
289
261
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
262
|
+
cryptfilter.Length = params[:key_size] >> 3
|
|
263
|
+
|
|
264
|
+
handler.CF[:StdCF] = cryptfilter
|
|
265
|
+
handler.StmF = handler.StrF = :StdCF
|
|
266
|
+
end
|
|
294
267
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
#
|
|
298
|
-
module Encryption
|
|
268
|
+
handler.set_passwords(ownerpasswd, userpasswd, doc_id)
|
|
269
|
+
encryption_key = handler.compute_user_encryption_key(userpasswd, doc_id)
|
|
299
270
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
271
|
+
file_info = get_trailer_info
|
|
272
|
+
file_info[:Encrypt] = self << handler
|
|
273
|
+
|
|
274
|
+
self.extend(Encryption::EncryptedDocument)
|
|
275
|
+
self.encryption_handler = handler
|
|
276
|
+
self.encryption_key = encryption_key
|
|
277
|
+
self.crypt_filters = crypt_filters
|
|
278
|
+
self.stm_filter = self.str_filter = :StdCF
|
|
279
|
+
|
|
280
|
+
self
|
|
281
|
+
end
|
|
305
282
|
end
|
|
306
283
|
|
|
307
284
|
#
|
|
308
|
-
#
|
|
285
|
+
# Module to provide support for encrypting and decrypting PDF documents.
|
|
309
286
|
#
|
|
310
|
-
|
|
311
|
-
if Origami::OPTIONS[:use_openssl]
|
|
312
|
-
OpenSSL::Random.random_bytes(n)
|
|
313
|
-
elsif RUBY_VERSION >= '1.9'
|
|
314
|
-
Random.new.bytes(n)
|
|
315
|
-
else
|
|
316
|
-
self.rand_bytes(n)
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
module EncryptedDocument
|
|
287
|
+
module Encryption
|
|
321
288
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
289
|
+
#
|
|
290
|
+
# Generates _n_ random bytes from a fast PRNG.
|
|
291
|
+
#
|
|
292
|
+
def self.rand_bytes(n)
|
|
293
|
+
Random.new.bytes(n)
|
|
294
|
+
end
|
|
326
295
|
|
|
327
|
-
|
|
296
|
+
#
|
|
297
|
+
# Generates _n_ random bytes from a crypto PRNG.
|
|
298
|
+
#
|
|
299
|
+
def self.strong_rand_bytes(n)
|
|
300
|
+
if Origami::OPTIONS[:use_openssl]
|
|
301
|
+
OpenSSL::Random.random_bytes(n)
|
|
302
|
+
else
|
|
303
|
+
SecureRandom.random_bytes(n)
|
|
304
|
+
end
|
|
305
|
+
end
|
|
328
306
|
|
|
329
|
-
|
|
307
|
+
module EncryptedDocument
|
|
308
|
+
attr_accessor :encryption_key
|
|
309
|
+
attr_accessor :encryption_handler
|
|
310
|
+
attr_accessor :str_filter, :stm_filter
|
|
311
|
+
attr_accessor :crypt_filters
|
|
330
312
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
obj.pre_build
|
|
335
|
-
obj.decrypt!
|
|
336
|
-
obj.decrypted = false # makes it believe no encryption pass is required
|
|
337
|
-
obj.post_build
|
|
313
|
+
# Get the encryption cipher from the crypt filter name.
|
|
314
|
+
def encryption_cipher(name)
|
|
315
|
+
@crypt_filters[name]
|
|
338
316
|
end
|
|
339
317
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
if obj.is_a?(ObjectStream)
|
|
344
|
-
obj.each do |subobj|
|
|
345
|
-
build(subobj, revision, options)
|
|
318
|
+
# Get the default string encryption cipher.
|
|
319
|
+
def string_encryption_cipher
|
|
320
|
+
encryption_cipher @str_filter
|
|
346
321
|
end
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
case obj
|
|
352
|
-
when String
|
|
353
|
-
if not obj.equal?(@encryption_dict[:U]) and
|
|
354
|
-
not obj.equal?(@encryption_dict[:O]) and
|
|
355
|
-
not obj.equal?(@encryption_dict[:UE]) and
|
|
356
|
-
not obj.equal?(@encryption_dict[:OE]) and
|
|
357
|
-
not obj.equal?(@encryption_dict[:Perms]) and
|
|
358
|
-
not (obj.parent.is_a?(Signature::DigitalSignature) and obj.equal?(obj.parent[:Contents])) and
|
|
359
|
-
not obj.indirect_parent.parent.is_a?(ObjectStream)
|
|
360
|
-
|
|
361
|
-
obj.extend(EncryptedString)
|
|
362
|
-
obj.decrypted = true
|
|
363
|
-
obj.encryption_handler = @encryption_dict
|
|
364
|
-
obj.encryption_key = @encryption_key
|
|
365
|
-
obj.algorithm = @str_algo
|
|
322
|
+
|
|
323
|
+
# Get the default stream encryption cipher.
|
|
324
|
+
def stream_encryption_cipher
|
|
325
|
+
encryption_cipher @stm_filter
|
|
366
326
|
end
|
|
367
327
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
328
|
+
private
|
|
329
|
+
|
|
330
|
+
def physicalize(options = {})
|
|
331
|
+
|
|
332
|
+
build = -> (obj, revision) do
|
|
333
|
+
if obj.is_a?(EncryptedObject)
|
|
334
|
+
if options[:decrypt] == true
|
|
335
|
+
obj.pre_build
|
|
336
|
+
obj.decrypt!
|
|
337
|
+
obj.decrypted = false # makes it believe no encryption pass is required
|
|
338
|
+
obj.post_build
|
|
339
|
+
|
|
340
|
+
return
|
|
341
|
+
end
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
if obj.is_a?(ObjectStream)
|
|
345
|
+
obj.each do |subobj|
|
|
346
|
+
build.call(subobj, revision)
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
obj.pre_build
|
|
351
|
+
|
|
352
|
+
case obj
|
|
353
|
+
when String
|
|
354
|
+
if not obj.equal?(@encryption_handler[:U]) and
|
|
355
|
+
not obj.equal?(@encryption_handler[:O]) and
|
|
356
|
+
not obj.equal?(@encryption_handler[:UE]) and
|
|
357
|
+
not obj.equal?(@encryption_handler[:OE]) and
|
|
358
|
+
not obj.equal?(@encryption_handler[:Perms]) and
|
|
359
|
+
not (obj.parent.is_a?(Signature::DigitalSignature) and
|
|
360
|
+
obj.equal?(obj.parent[:Contents])) and
|
|
361
|
+
not obj.indirect_parent.parent.is_a?(ObjectStream)
|
|
362
|
+
|
|
363
|
+
unless obj.is_a?(EncryptedString)
|
|
364
|
+
obj.extend(EncryptedString)
|
|
365
|
+
obj.decrypted = true
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
when Stream
|
|
370
|
+
return if obj.is_a?(XRefStream)
|
|
371
|
+
return if obj.equal?(self.Catalog.Metadata) and not @encryption_handler.EncryptMetadata
|
|
372
|
+
|
|
373
|
+
unless obj.is_a?(EncryptedStream)
|
|
374
|
+
obj.extend(EncryptedStream)
|
|
375
|
+
obj.decrypted = true
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
when Dictionary, Array
|
|
379
|
+
obj.map! do |subobj|
|
|
380
|
+
if subobj.indirect?
|
|
381
|
+
if get_object(subobj.reference)
|
|
382
|
+
subobj.reference
|
|
383
|
+
else
|
|
384
|
+
ref = add_to_revision(subobj, revision)
|
|
385
|
+
build.call(subobj, revision)
|
|
386
|
+
ref
|
|
387
|
+
end
|
|
388
|
+
else
|
|
389
|
+
subobj
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
obj.each do |subobj|
|
|
394
|
+
build.call(subobj, revision)
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
obj.post_build
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# stack up every root objects
|
|
402
|
+
indirect_objects_by_rev.each do |obj, revision|
|
|
403
|
+
build.call(obj, revision)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# remove encrypt dictionary if requested
|
|
407
|
+
if options[:decrypt]
|
|
408
|
+
delete_object(get_trailer_info[:Encrypt])
|
|
409
|
+
get_trailer_info[:Encrypt] = nil
|
|
386
410
|
end
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
end
|
|
411
|
+
|
|
412
|
+
self
|
|
390
413
|
end
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
#
|
|
417
|
+
# Module for encrypted PDF objects.
|
|
418
|
+
#
|
|
419
|
+
module EncryptedObject #:nodoc
|
|
420
|
+
attr_accessor :decrypted
|
|
421
|
+
|
|
422
|
+
def post_build
|
|
423
|
+
encrypt!
|
|
424
|
+
|
|
425
|
+
super
|
|
394
426
|
end
|
|
395
|
-
end
|
|
396
427
|
|
|
397
|
-
|
|
398
|
-
end
|
|
399
|
-
|
|
400
|
-
# stack up every root objects
|
|
401
|
-
indirect_objects_by_rev.each do |obj, revision|
|
|
402
|
-
build(obj, revision, options)
|
|
403
|
-
end
|
|
428
|
+
private
|
|
404
429
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
get_trailer_info[:Encrypt] = nil
|
|
409
|
-
end
|
|
410
|
-
|
|
411
|
-
self
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
end
|
|
430
|
+
def compute_object_key(cipher)
|
|
431
|
+
doc = self.document
|
|
432
|
+
raise EncryptionError, "Document is not encrypted" unless doc.is_a?(EncryptedDocument)
|
|
415
433
|
|
|
416
|
-
|
|
417
|
-
# Module for encrypted PDF objects.
|
|
418
|
-
#
|
|
419
|
-
module EncryptedObject #:nodoc
|
|
420
|
-
|
|
421
|
-
attr_writer :encryption_key
|
|
422
|
-
attr_writer :algorithm
|
|
423
|
-
attr_writer :encryption_handler
|
|
424
|
-
attr_accessor :decrypted
|
|
425
|
-
|
|
426
|
-
def self.extended(obj)
|
|
427
|
-
obj.decrypted = false
|
|
428
|
-
end
|
|
429
|
-
|
|
430
|
-
def post_build
|
|
431
|
-
encrypt!
|
|
432
|
-
|
|
433
|
-
super
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
private
|
|
437
|
-
|
|
438
|
-
def compute_object_key
|
|
439
|
-
if @encryption_handler.V < 5
|
|
440
|
-
parent = self.indirect_parent
|
|
441
|
-
no, gen = parent.no, parent.generation
|
|
442
|
-
k = @encryption_key + [no].pack("I")[0..2] + [gen].pack("I")[0..1]
|
|
443
|
-
|
|
444
|
-
key_len = (k.length > 16) ? 16 : k.length
|
|
445
|
-
k << "sAlT" if @algorithm == Encryption::AES
|
|
446
|
-
|
|
447
|
-
Digest::MD5.digest(k)[0, key_len]
|
|
448
|
-
else
|
|
449
|
-
@encryption_key
|
|
450
|
-
end
|
|
451
|
-
end
|
|
434
|
+
encryption_key = doc.encryption_key
|
|
452
435
|
|
|
453
|
-
|
|
436
|
+
if doc.encryption_handler.V < 5
|
|
437
|
+
parent = self.indirect_parent
|
|
438
|
+
no, gen = parent.no, parent.generation
|
|
439
|
+
k = encryption_key + [no].pack("I")[0..2] + [gen].pack("I")[0..1]
|
|
454
440
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
key = compute_object_key
|
|
464
|
-
|
|
465
|
-
encrypted_data =
|
|
466
|
-
if @algorithm == ARC4 or @algorithm == Identity
|
|
467
|
-
@algorithm.encrypt(key, self.value)
|
|
468
|
-
else
|
|
469
|
-
iv = Encryption.rand_bytes(AES::BLOCKSIZE)
|
|
470
|
-
@algorithm.encrypt(key, iv, self.value)
|
|
471
|
-
end
|
|
472
|
-
|
|
473
|
-
@decrypted = false
|
|
474
|
-
|
|
475
|
-
self.replace(encrypted_data)
|
|
476
|
-
self.freeze
|
|
477
|
-
end
|
|
478
|
-
|
|
479
|
-
self
|
|
480
|
-
end
|
|
481
|
-
|
|
482
|
-
def decrypt!
|
|
483
|
-
unless @decrypted
|
|
484
|
-
key = compute_object_key
|
|
485
|
-
self.replace(@algorithm.decrypt(key, self.to_str))
|
|
486
|
-
@decrypted = true
|
|
441
|
+
key_len = (k.length > 16) ? 16 : k.length
|
|
442
|
+
k << "sAlT" if cipher == Encryption::AES
|
|
443
|
+
|
|
444
|
+
Digest::MD5.digest(k)[0, key_len]
|
|
445
|
+
else
|
|
446
|
+
encryption_key
|
|
447
|
+
end
|
|
448
|
+
end
|
|
487
449
|
end
|
|
488
450
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
451
|
+
#
|
|
452
|
+
# Module for encrypted String.
|
|
453
|
+
#
|
|
454
|
+
module EncryptedString
|
|
455
|
+
include EncryptedObject
|
|
492
456
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
module EncryptedStream
|
|
497
|
-
include EncryptedObject
|
|
457
|
+
def self.extended(obj)
|
|
458
|
+
obj.decrypted = false
|
|
459
|
+
end
|
|
498
460
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
encode!
|
|
461
|
+
def encrypt!
|
|
462
|
+
return self unless @decrypted
|
|
502
463
|
|
|
503
|
-
|
|
464
|
+
cipher = self.document.string_encryption_cipher
|
|
465
|
+
raise EncryptionError, "Cannot find string encryption filter" if cipher.nil?
|
|
504
466
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
467
|
+
key = compute_object_key(cipher)
|
|
468
|
+
|
|
469
|
+
encrypted_data =
|
|
470
|
+
if cipher == RC4 or cipher == Identity
|
|
471
|
+
cipher.encrypt(key, self.value)
|
|
472
|
+
else
|
|
473
|
+
iv = Encryption.rand_bytes(AES::BLOCKSIZE)
|
|
474
|
+
cipher.encrypt(key, iv, self.value)
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
@decrypted = false
|
|
478
|
+
|
|
479
|
+
self.replace(encrypted_data)
|
|
480
|
+
self.freeze
|
|
481
|
+
|
|
482
|
+
self
|
|
511
483
|
end
|
|
512
484
|
|
|
513
|
-
|
|
485
|
+
def decrypt!
|
|
486
|
+
return self if @decrypted
|
|
514
487
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
end
|
|
488
|
+
cipher = self.document.string_encryption_cipher
|
|
489
|
+
raise EncryptionError, "Cannot find string encryption filter" if cipher.nil?
|
|
518
490
|
|
|
519
|
-
|
|
520
|
-
end
|
|
491
|
+
key = compute_object_key(cipher)
|
|
521
492
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
key = compute_object_key
|
|
493
|
+
self.replace(cipher.decrypt(key, self.to_str))
|
|
494
|
+
@decrypted = true
|
|
525
495
|
|
|
526
|
-
|
|
527
|
-
|
|
496
|
+
self
|
|
497
|
+
end
|
|
528
498
|
end
|
|
529
499
|
|
|
530
|
-
|
|
531
|
-
|
|
500
|
+
#
|
|
501
|
+
# Module for encrypted Stream.
|
|
502
|
+
#
|
|
503
|
+
module EncryptedStream
|
|
504
|
+
include EncryptedObject
|
|
532
505
|
|
|
533
|
-
|
|
506
|
+
def self.extended(obj)
|
|
507
|
+
obj.decrypted = false
|
|
508
|
+
end
|
|
534
509
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
#
|
|
538
|
-
module Identity
|
|
539
|
-
def Identity.encrypt(key, data)
|
|
540
|
-
data
|
|
541
|
-
end
|
|
542
|
-
|
|
543
|
-
def Identity.decrypt(key, data)
|
|
544
|
-
data
|
|
545
|
-
end
|
|
546
|
-
end
|
|
510
|
+
def encrypt!
|
|
511
|
+
return self unless @decrypted
|
|
547
512
|
|
|
548
|
-
|
|
549
|
-
# Pure Ruby implementation of the aRC4 symmetric algorithm
|
|
550
|
-
#
|
|
551
|
-
class ARC4
|
|
552
|
-
|
|
553
|
-
#
|
|
554
|
-
# Encrypts data using the given key
|
|
555
|
-
#
|
|
556
|
-
def ARC4.encrypt(key, data)
|
|
557
|
-
ARC4.new(key).encrypt(data)
|
|
558
|
-
end
|
|
559
|
-
|
|
560
|
-
#
|
|
561
|
-
# Decrypts data using the given key
|
|
562
|
-
#
|
|
563
|
-
def ARC4.decrypt(key, data)
|
|
564
|
-
ARC4.new(key).decrypt(data)
|
|
565
|
-
end
|
|
566
|
-
|
|
567
|
-
#
|
|
568
|
-
# Creates and initialises a new aRC4 generator using given key
|
|
569
|
-
#
|
|
570
|
-
def initialize(key)
|
|
571
|
-
if Origami::OPTIONS[:use_openssl]
|
|
572
|
-
@key = key
|
|
573
|
-
else
|
|
574
|
-
@state = init(key)
|
|
575
|
-
end
|
|
576
|
-
end
|
|
577
|
-
|
|
578
|
-
#
|
|
579
|
-
# Encrypt/decrypt data with the aRC4 encryption algorithm
|
|
580
|
-
#
|
|
581
|
-
def cipher(data)
|
|
582
|
-
return "" if data.empty?
|
|
583
|
-
|
|
584
|
-
if Origami::OPTIONS[:use_openssl]
|
|
585
|
-
rc4 = OpenSSL::Cipher::RC4.new.encrypt
|
|
586
|
-
rc4.key_len = @key.length
|
|
587
|
-
rc4.key = @key
|
|
588
|
-
|
|
589
|
-
output = rc4.update(data) << rc4.final
|
|
590
|
-
else
|
|
591
|
-
output = ""
|
|
592
|
-
i, j = 0, 0
|
|
593
|
-
data.each_byte do |byte|
|
|
594
|
-
i = i.succ & 0xFF
|
|
595
|
-
j = (j + @state[i]) & 0xFF
|
|
596
|
-
|
|
597
|
-
@state[i], @state[j] = @state[j], @state[i]
|
|
598
|
-
|
|
599
|
-
output << (@state[@state[i] + @state[j] & 0xFF] ^ byte).chr
|
|
600
|
-
end
|
|
601
|
-
end
|
|
602
|
-
|
|
603
|
-
output
|
|
604
|
-
end
|
|
605
|
-
|
|
606
|
-
alias encrypt cipher
|
|
607
|
-
alias decrypt cipher
|
|
608
|
-
|
|
609
|
-
private
|
|
610
|
-
|
|
611
|
-
def init(key) #:nodoc:
|
|
612
|
-
|
|
613
|
-
state = (0..255).to_a
|
|
614
|
-
|
|
615
|
-
j = 0
|
|
616
|
-
256.times do |i|
|
|
617
|
-
j = ( j + state[i] + key[i % key.size].ord ) & 0xFF
|
|
618
|
-
state[i], state[j] = state[j], state[i]
|
|
619
|
-
end
|
|
620
|
-
|
|
621
|
-
state
|
|
622
|
-
end
|
|
513
|
+
encode!
|
|
623
514
|
|
|
624
|
-
|
|
515
|
+
if self.filters.first == :Crypt
|
|
516
|
+
params = decode_params.first
|
|
625
517
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
NROWS = 4
|
|
633
|
-
NCOLS = 4
|
|
634
|
-
BLOCKSIZE = NROWS * NCOLS
|
|
635
|
-
|
|
636
|
-
ROUNDS =
|
|
637
|
-
{
|
|
638
|
-
16 => 10,
|
|
639
|
-
24 => 12,
|
|
640
|
-
32 => 14
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
#
|
|
644
|
-
# Rijndael S-box
|
|
645
|
-
#
|
|
646
|
-
SBOX =
|
|
647
|
-
[
|
|
648
|
-
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
|
649
|
-
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
|
650
|
-
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
|
651
|
-
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
|
652
|
-
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
|
653
|
-
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
|
654
|
-
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
|
655
|
-
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
|
656
|
-
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
|
657
|
-
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
|
658
|
-
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
|
659
|
-
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
|
660
|
-
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
|
661
|
-
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
|
662
|
-
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
|
663
|
-
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
|
|
664
|
-
]
|
|
665
|
-
|
|
666
|
-
#
|
|
667
|
-
# Inverse of the Rijndael S-box
|
|
668
|
-
#
|
|
669
|
-
RSBOX =
|
|
670
|
-
[
|
|
671
|
-
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
|
672
|
-
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
|
673
|
-
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
|
674
|
-
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
|
675
|
-
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
|
676
|
-
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
|
677
|
-
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
|
678
|
-
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
|
679
|
-
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
|
680
|
-
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
|
681
|
-
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
|
682
|
-
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
|
683
|
-
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
|
684
|
-
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
|
685
|
-
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
|
686
|
-
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
|
|
687
|
-
]
|
|
688
|
-
|
|
689
|
-
RCON =
|
|
690
|
-
[
|
|
691
|
-
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
|
|
692
|
-
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
|
|
693
|
-
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
|
|
694
|
-
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
|
|
695
|
-
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
|
|
696
|
-
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
|
|
697
|
-
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
|
|
698
|
-
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
|
|
699
|
-
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
|
|
700
|
-
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
|
|
701
|
-
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
|
|
702
|
-
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
|
|
703
|
-
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
|
|
704
|
-
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
|
|
705
|
-
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
|
|
706
|
-
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
|
|
707
|
-
]
|
|
708
|
-
|
|
709
|
-
attr_writer :iv
|
|
710
|
-
|
|
711
|
-
def AES.encrypt(key, iv, data)
|
|
712
|
-
AES.new(key, iv).encrypt(data)
|
|
713
|
-
end
|
|
714
|
-
|
|
715
|
-
def AES.decrypt(key, data)
|
|
716
|
-
AES.new(key, nil).decrypt(data)
|
|
717
|
-
end
|
|
718
|
-
|
|
719
|
-
def initialize(key, iv, use_padding = true)
|
|
720
|
-
unless key.size == 16 or key.size == 24 or key.size == 32
|
|
721
|
-
raise EncryptionError, "Key must have a length of 128, 192 or 256 bits."
|
|
722
|
-
end
|
|
518
|
+
if params.is_a?(Dictionary) and params.Name.is_a?(Name)
|
|
519
|
+
crypt_filter = params.Name.value
|
|
520
|
+
else
|
|
521
|
+
crypt_filter = :Identity
|
|
522
|
+
end
|
|
723
523
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
524
|
+
cipher = self.document.encryption_cipher(crypt_filter)
|
|
525
|
+
else
|
|
526
|
+
cipher = self.document.stream_encryption_cipher
|
|
527
|
+
end
|
|
528
|
+
raise EncryptionError, "Cannot find stream encryption filter" if cipher.nil?
|
|
727
529
|
|
|
728
|
-
|
|
729
|
-
@iv = iv
|
|
730
|
-
@use_padding = use_padding
|
|
731
|
-
end
|
|
530
|
+
key = compute_object_key(cipher)
|
|
732
531
|
|
|
733
|
-
|
|
532
|
+
@encoded_data =
|
|
533
|
+
if cipher == RC4 or cipher == Identity
|
|
534
|
+
cipher.encrypt(key, self.encoded_data)
|
|
535
|
+
else
|
|
536
|
+
iv = Encryption.rand_bytes(AES::BLOCKSIZE)
|
|
537
|
+
cipher.encrypt(key, iv, @encoded_data)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
@decrypted = false
|
|
541
|
+
|
|
542
|
+
@encoded_data.freeze
|
|
543
|
+
self.freeze
|
|
544
|
+
|
|
545
|
+
self
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
def decrypt!
|
|
549
|
+
return self if @decrypted
|
|
550
|
+
|
|
551
|
+
if self.filters.first == :Crypt
|
|
552
|
+
params = decode_params.first
|
|
553
|
+
|
|
554
|
+
if params.is_a?(Dictionary) and params.Name.is_a?(Name)
|
|
555
|
+
crypt_filter = params.Name.value
|
|
556
|
+
else
|
|
557
|
+
crypt_filter = :Identity
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
cipher = self.document.encryption_cipher(crypt_filter)
|
|
561
|
+
else
|
|
562
|
+
cipher = self.document.stream_encryption_cipher
|
|
563
|
+
end
|
|
564
|
+
raise EncryptionError, "Cannot find stream encryption filter" if cipher.nil?
|
|
734
565
|
|
|
735
|
-
|
|
736
|
-
|
|
566
|
+
key = compute_object_key(cipher)
|
|
567
|
+
|
|
568
|
+
self.encoded_data = cipher.decrypt(key, @encoded_data)
|
|
569
|
+
@decrypted = true
|
|
570
|
+
|
|
571
|
+
self
|
|
572
|
+
end
|
|
737
573
|
end
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
574
|
+
|
|
575
|
+
#
|
|
576
|
+
# Identity transformation.
|
|
577
|
+
#
|
|
578
|
+
module Identity
|
|
579
|
+
def Identity.encrypt(key, data)
|
|
580
|
+
data
|
|
581
|
+
end
|
|
582
|
+
|
|
583
|
+
def Identity.decrypt(key, data)
|
|
584
|
+
data
|
|
585
|
+
end
|
|
742
586
|
end
|
|
743
587
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
aes.padding = 0
|
|
588
|
+
#
|
|
589
|
+
# Pure Ruby implementation of the RC4 symmetric algorithm
|
|
590
|
+
#
|
|
591
|
+
class RC4
|
|
749
592
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
593
|
+
#
|
|
594
|
+
# Encrypts data using the given key
|
|
595
|
+
#
|
|
596
|
+
def RC4.encrypt(key, data)
|
|
597
|
+
RC4.new(key).encrypt(data)
|
|
598
|
+
end
|
|
755
599
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
600
|
+
#
|
|
601
|
+
# Decrypts data using the given key
|
|
602
|
+
#
|
|
603
|
+
def RC4.decrypt(key, data)
|
|
604
|
+
RC4.new(key).decrypt(data)
|
|
605
|
+
end
|
|
759
606
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
607
|
+
#
|
|
608
|
+
# Creates and initialises a new RC4 generator using given key
|
|
609
|
+
#
|
|
610
|
+
def initialize(key)
|
|
611
|
+
if Origami::OPTIONS[:use_openssl]
|
|
612
|
+
@key = key
|
|
613
|
+
else
|
|
614
|
+
@state = init(key)
|
|
615
|
+
end
|
|
764
616
|
end
|
|
765
617
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
618
|
+
#
|
|
619
|
+
# Encrypt/decrypt data with the RC4 encryption algorithm
|
|
620
|
+
#
|
|
621
|
+
def cipher(data)
|
|
622
|
+
return "" if data.empty?
|
|
770
623
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
624
|
+
if Origami::OPTIONS[:use_openssl]
|
|
625
|
+
rc4 = OpenSSL::Cipher::RC4.new.encrypt
|
|
626
|
+
rc4.key_len = @key.length
|
|
627
|
+
rc4.key = @key
|
|
628
|
+
|
|
629
|
+
output = rc4.update(data) << rc4.final
|
|
630
|
+
else
|
|
631
|
+
output = ""
|
|
632
|
+
i, j = 0, 0
|
|
633
|
+
data.each_byte do |byte|
|
|
634
|
+
i = i.succ & 0xFF
|
|
635
|
+
j = (j + @state[i]) & 0xFF
|
|
636
|
+
|
|
637
|
+
@state[i], @state[j] = @state[j], @state[i]
|
|
774
638
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
639
|
+
output << (@state[@state[i] + @state[j] & 0xFF] ^ byte).chr
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
output
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
alias encrypt cipher
|
|
647
|
+
alias decrypt cipher
|
|
648
|
+
|
|
649
|
+
private
|
|
650
|
+
|
|
651
|
+
def init(key) #:nodoc:
|
|
652
|
+
state = (0..255).to_a
|
|
653
|
+
|
|
654
|
+
j = 0
|
|
655
|
+
256.times do |i|
|
|
656
|
+
j = ( j + state[i] + key[i % key.size].ord ) & 0xFF
|
|
657
|
+
state[i], state[j] = state[j], state[i]
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
state
|
|
661
|
+
end
|
|
779
662
|
end
|
|
780
663
|
|
|
781
|
-
|
|
664
|
+
#
|
|
665
|
+
# Pure Ruby implementation of the AES symmetric algorithm.
|
|
666
|
+
# Using mode CBC.
|
|
667
|
+
#
|
|
668
|
+
class AES
|
|
669
|
+
NROWS = 4
|
|
670
|
+
NCOLS = 4
|
|
671
|
+
BLOCKSIZE = NROWS * NCOLS
|
|
672
|
+
|
|
673
|
+
ROUNDS =
|
|
674
|
+
{
|
|
675
|
+
16 => 10,
|
|
676
|
+
24 => 12,
|
|
677
|
+
32 => 14
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
#
|
|
681
|
+
# Rijndael S-box
|
|
682
|
+
#
|
|
683
|
+
SBOX =
|
|
684
|
+
[
|
|
685
|
+
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
|
686
|
+
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
|
687
|
+
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
|
688
|
+
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
|
689
|
+
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
|
690
|
+
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
|
691
|
+
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
|
692
|
+
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
|
693
|
+
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
|
694
|
+
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
|
695
|
+
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
|
696
|
+
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
|
697
|
+
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
|
698
|
+
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
|
699
|
+
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
|
700
|
+
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
|
|
701
|
+
]
|
|
702
|
+
|
|
703
|
+
#
|
|
704
|
+
# Inverse of the Rijndael S-box
|
|
705
|
+
#
|
|
706
|
+
RSBOX =
|
|
707
|
+
[
|
|
708
|
+
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
|
|
709
|
+
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
|
|
710
|
+
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
|
|
711
|
+
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
|
|
712
|
+
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
|
|
713
|
+
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
|
|
714
|
+
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
|
|
715
|
+
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
|
|
716
|
+
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
|
|
717
|
+
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
|
|
718
|
+
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
|
|
719
|
+
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
|
|
720
|
+
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
|
|
721
|
+
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
|
|
722
|
+
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
|
|
723
|
+
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
|
|
724
|
+
]
|
|
725
|
+
|
|
726
|
+
RCON =
|
|
727
|
+
[
|
|
728
|
+
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a,
|
|
729
|
+
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39,
|
|
730
|
+
0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a,
|
|
731
|
+
0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8,
|
|
732
|
+
0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef,
|
|
733
|
+
0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc,
|
|
734
|
+
0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b,
|
|
735
|
+
0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3,
|
|
736
|
+
0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94,
|
|
737
|
+
0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20,
|
|
738
|
+
0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35,
|
|
739
|
+
0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f,
|
|
740
|
+
0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04,
|
|
741
|
+
0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63,
|
|
742
|
+
0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd,
|
|
743
|
+
0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb
|
|
744
|
+
]
|
|
745
|
+
|
|
746
|
+
attr_writer :iv
|
|
747
|
+
|
|
748
|
+
def AES.encrypt(key, iv, data)
|
|
749
|
+
AES.new(key, iv).encrypt(data)
|
|
750
|
+
end
|
|
782
751
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
752
|
+
def AES.decrypt(key, data)
|
|
753
|
+
AES.new(key, nil).decrypt(data)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
def initialize(key, iv, use_padding = true)
|
|
757
|
+
unless key.size == 16 or key.size == 24 or key.size == 32
|
|
758
|
+
raise EncryptionError, "Key must have a length of 128, 192 or 256 bits."
|
|
759
|
+
end
|
|
788
760
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
plainblock = []
|
|
793
|
-
prev_cipherblock = []
|
|
794
|
-
nblocks = data.size / BLOCKSIZE
|
|
761
|
+
if not iv.nil? and iv.size != BLOCKSIZE
|
|
762
|
+
raise EncryptionError, "Initialization vector must have a length of #{BLOCKSIZE} bytes."
|
|
763
|
+
end
|
|
795
764
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
765
|
+
@key = key
|
|
766
|
+
@iv = iv
|
|
767
|
+
@use_padding = use_padding
|
|
768
|
+
end
|
|
799
769
|
|
|
800
|
-
|
|
770
|
+
def encrypt(data)
|
|
771
|
+
if @iv.nil?
|
|
772
|
+
raise EncryptionError, "No initialization vector has been set."
|
|
773
|
+
end
|
|
801
774
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
775
|
+
if @use_padding
|
|
776
|
+
padlen = BLOCKSIZE - (data.size % BLOCKSIZE)
|
|
777
|
+
data << (padlen.chr * padlen)
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
if Origami::OPTIONS[:use_openssl]
|
|
781
|
+
aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").encrypt
|
|
782
|
+
aes.iv = @iv
|
|
783
|
+
aes.key = @key
|
|
784
|
+
aes.padding = 0
|
|
785
|
+
|
|
786
|
+
@iv + aes.update(data) + aes.final
|
|
787
|
+
else
|
|
788
|
+
cipher = []
|
|
789
|
+
cipherblock = []
|
|
790
|
+
nblocks = data.size / BLOCKSIZE
|
|
791
|
+
|
|
792
|
+
first_round = true
|
|
793
|
+
nblocks.times do |n|
|
|
794
|
+
plainblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
|
|
795
|
+
|
|
796
|
+
if first_round
|
|
797
|
+
BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
|
|
798
|
+
else
|
|
799
|
+
BLOCKSIZE.times do |i| plainblock[i] ^= cipherblock[i] end
|
|
800
|
+
end
|
|
801
|
+
|
|
802
|
+
first_round = false
|
|
803
|
+
cipherblock = aes_encrypt(plainblock)
|
|
804
|
+
cipher.concat(cipherblock)
|
|
805
|
+
end
|
|
806
|
+
|
|
807
|
+
@iv + cipher.pack("C*")
|
|
808
|
+
end
|
|
806
809
|
end
|
|
807
810
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
end
|
|
811
|
+
def decrypt(data)
|
|
812
|
+
unless data.size % BLOCKSIZE == 0
|
|
813
|
+
raise EncryptionError, "Data must be 16-bytes padded (data size = #{data.size} bytes)"
|
|
814
|
+
end
|
|
813
815
|
|
|
814
|
-
|
|
815
|
-
padlen = plain[-1]
|
|
816
|
-
unless (1..16) === padlen
|
|
817
|
-
raise EncryptionError, "Incorrect padding length : #{padlen}"
|
|
818
|
-
end
|
|
819
|
-
|
|
820
|
-
padlen.times do
|
|
821
|
-
pad = plain.pop
|
|
822
|
-
raise EncryptionError,
|
|
823
|
-
"Incorrect padding byte : 0x#{pad.to_s 16}" if pad != padlen
|
|
824
|
-
end
|
|
825
|
-
end
|
|
816
|
+
@iv = data.slice!(0, BLOCKSIZE)
|
|
826
817
|
|
|
827
|
-
|
|
828
|
-
|
|
818
|
+
if Origami::OPTIONS[:use_openssl]
|
|
819
|
+
aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").decrypt
|
|
820
|
+
aes.iv = @iv
|
|
821
|
+
aes.key = @key
|
|
822
|
+
aes.padding = 0
|
|
829
823
|
|
|
830
|
-
|
|
824
|
+
plain = (aes.update(data) + aes.final).unpack("C*")
|
|
825
|
+
else
|
|
826
|
+
plain = []
|
|
827
|
+
plainblock = []
|
|
828
|
+
prev_cipherblock = []
|
|
829
|
+
nblocks = data.size / BLOCKSIZE
|
|
830
|
+
|
|
831
|
+
first_round = true
|
|
832
|
+
nblocks.times do |n|
|
|
833
|
+
cipherblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
|
|
834
|
+
|
|
835
|
+
plainblock = aes_decrypt(cipherblock)
|
|
836
|
+
|
|
837
|
+
if first_round
|
|
838
|
+
BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
|
|
839
|
+
else
|
|
840
|
+
BLOCKSIZE.times do |i| plainblock[i] ^= prev_cipherblock[i] end
|
|
841
|
+
end
|
|
842
|
+
|
|
843
|
+
first_round = false
|
|
844
|
+
prev_cipherblock = cipherblock
|
|
845
|
+
plain.concat(plainblock)
|
|
846
|
+
end
|
|
847
|
+
end
|
|
831
848
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
849
|
+
if @use_padding
|
|
850
|
+
padlen = plain[-1]
|
|
851
|
+
unless (1..16) === padlen
|
|
852
|
+
raise EncryptionError, "Incorrect padding length : #{padlen}"
|
|
853
|
+
end
|
|
835
854
|
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
855
|
+
padlen.times do
|
|
856
|
+
pad = plain.pop
|
|
857
|
+
raise EncryptionError, "Incorrect padding byte : 0x#{pad.to_s 16}" if pad != padlen
|
|
858
|
+
end
|
|
859
|
+
end
|
|
839
860
|
|
|
840
|
-
|
|
841
|
-
|
|
861
|
+
plain.pack("C*")
|
|
862
|
+
end
|
|
842
863
|
|
|
843
|
-
|
|
844
|
-
p ^= a if b[0] == 1
|
|
845
|
-
highBit = a[7]
|
|
846
|
-
a <<= 1
|
|
847
|
-
a ^= 0x1b if highBit == 1
|
|
848
|
-
b >>= 1
|
|
849
|
-
end
|
|
864
|
+
private
|
|
850
865
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
def scheduleCore(word, iter) #:nodoc:
|
|
855
|
-
rol(word)
|
|
856
|
-
word.map! do |byte| SBOX[byte] end
|
|
857
|
-
word[0] ^= RCON[iter]
|
|
858
|
-
|
|
859
|
-
word
|
|
860
|
-
end
|
|
861
|
-
|
|
862
|
-
def transpose(m) #:nodoc:
|
|
863
|
-
[
|
|
864
|
-
m[NROWS * 0, NROWS],
|
|
865
|
-
m[NROWS * 1, NROWS],
|
|
866
|
-
m[NROWS * 2, NROWS],
|
|
867
|
-
m[NROWS * 3, NROWS]
|
|
868
|
-
].transpose.flatten
|
|
869
|
-
end
|
|
870
|
-
|
|
871
|
-
#
|
|
872
|
-
# AES round methods.
|
|
873
|
-
#
|
|
874
|
-
|
|
875
|
-
def createRoundKey(expandedKey, round = 0) #:nodoc:
|
|
876
|
-
transpose(expandedKey[round * BLOCKSIZE, BLOCKSIZE])
|
|
877
|
-
end
|
|
878
|
-
|
|
879
|
-
def addRoundKey(roundKey) #:nodoc:
|
|
880
|
-
BLOCKSIZE.times do |i| @state[i] ^= roundKey[i] end
|
|
881
|
-
end
|
|
882
|
-
|
|
883
|
-
def subBytes #:nodoc:
|
|
884
|
-
BLOCKSIZE.times do |i| @state[i] = SBOX[ @state[i] ] end
|
|
885
|
-
end
|
|
886
|
-
|
|
887
|
-
def rsubBytes #:nodoc:
|
|
888
|
-
BLOCKSIZE.times do |i| @state[i] = RSBOX[ @state[i] ] end
|
|
889
|
-
end
|
|
890
|
-
|
|
891
|
-
def shiftRows #:nodoc:
|
|
892
|
-
NROWS.times do |i|
|
|
893
|
-
@state[i * NCOLS, NCOLS] = rol(@state[i * NCOLS, NCOLS], i)
|
|
894
|
-
end
|
|
895
|
-
end
|
|
866
|
+
def rol(row, n = 1) #:nodoc
|
|
867
|
+
n.times do row.push row.shift end ; row
|
|
868
|
+
end
|
|
896
869
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
end
|
|
901
|
-
end
|
|
902
|
-
|
|
903
|
-
def mixColumnWithField(column, field) #:nodoc:
|
|
904
|
-
p = field
|
|
905
|
-
|
|
906
|
-
column[0], column[1], column[2], column[3] =
|
|
907
|
-
galoisMult(column[0], p[0]) ^ galoisMult(column[3], p[1]) ^ galoisMult(column[2], p[2]) ^ galoisMult(column[1], p[3]),
|
|
908
|
-
galoisMult(column[1], p[0]) ^ galoisMult(column[0], p[1]) ^ galoisMult(column[3], p[2]) ^ galoisMult(column[2], p[3]),
|
|
909
|
-
galoisMult(column[2], p[0]) ^ galoisMult(column[1], p[1]) ^ galoisMult(column[0], p[2]) ^ galoisMult(column[3], p[3]),
|
|
910
|
-
galoisMult(column[3], p[0]) ^ galoisMult(column[2], p[1]) ^ galoisMult(column[1], p[2]) ^ galoisMult(column[0], p[3])
|
|
911
|
-
end
|
|
912
|
-
|
|
913
|
-
def mixColumn(column) #:nodoc:
|
|
914
|
-
mixColumnWithField(column, [ 2, 1, 1, 3 ])
|
|
915
|
-
end
|
|
916
|
-
|
|
917
|
-
def rmixColumn(column) #:nodoc:
|
|
918
|
-
mixColumnWithField(column, [ 14, 9, 13, 11 ])
|
|
919
|
-
end
|
|
920
|
-
|
|
921
|
-
def mixColumns #:nodoc:
|
|
922
|
-
NCOLS.times do |c|
|
|
923
|
-
column = []
|
|
924
|
-
NROWS.times do |r| column << @state[c + r * NCOLS] end
|
|
925
|
-
mixColumn(column)
|
|
926
|
-
NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
|
|
927
|
-
end
|
|
928
|
-
end
|
|
929
|
-
|
|
930
|
-
def rmixColumns #:nodoc:
|
|
931
|
-
NCOLS.times do |c|
|
|
932
|
-
column = []
|
|
933
|
-
NROWS.times do |r| column << @state[c + r * NCOLS] end
|
|
934
|
-
rmixColumn(column)
|
|
935
|
-
NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
|
|
936
|
-
end
|
|
937
|
-
end
|
|
870
|
+
def ror(row, n = 1) #:nodoc:
|
|
871
|
+
n.times do row.unshift row.pop end ; row
|
|
872
|
+
end
|
|
938
873
|
|
|
939
|
-
|
|
874
|
+
def galois_mult(a, b) #:nodoc:
|
|
875
|
+
p = 0
|
|
940
876
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
877
|
+
8.times do
|
|
878
|
+
p ^= a if b[0] == 1
|
|
879
|
+
highBit = a[7]
|
|
880
|
+
a <<= 1
|
|
881
|
+
a ^= 0x1b if highBit == 1
|
|
882
|
+
b >>= 1
|
|
883
|
+
end
|
|
946
884
|
|
|
947
|
-
|
|
948
|
-
|
|
885
|
+
p % 256
|
|
886
|
+
end
|
|
949
887
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
888
|
+
def schedule_core(word, iter) #:nodoc:
|
|
889
|
+
rol(word)
|
|
890
|
+
word.map! do |byte| SBOX[byte] end
|
|
891
|
+
word[0] ^= RCON[iter]
|
|
954
892
|
|
|
955
|
-
|
|
893
|
+
word
|
|
894
|
+
end
|
|
956
895
|
|
|
957
|
-
|
|
958
|
-
|
|
896
|
+
def transpose(m) #:nodoc:
|
|
897
|
+
[
|
|
898
|
+
m[NROWS * 0, NROWS],
|
|
899
|
+
m[NROWS * 1, NROWS],
|
|
900
|
+
m[NROWS * 2, NROWS],
|
|
901
|
+
m[NROWS * 3, NROWS]
|
|
902
|
+
].transpose.flatten
|
|
903
|
+
end
|
|
959
904
|
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
def aesRound(roundKey) #:nodoc:
|
|
964
|
-
subBytes
|
|
965
|
-
#puts "after subBytes: #{@state.inspect}"
|
|
966
|
-
shiftRows
|
|
967
|
-
#puts "after shiftRows: #{@state.inspect}"
|
|
968
|
-
mixColumns
|
|
969
|
-
#puts "after mixColumns: #{@state.inspect}"
|
|
970
|
-
addRoundKey(roundKey)
|
|
971
|
-
#puts "roundKey = #{roundKey.inspect}"
|
|
972
|
-
#puts "after addRoundKey: #{@state.inspect}"
|
|
973
|
-
end
|
|
974
|
-
|
|
975
|
-
def raesRound(roundKey) #:nodoc:
|
|
976
|
-
addRoundKey(roundKey)
|
|
977
|
-
rmixColumns
|
|
978
|
-
rshiftRows
|
|
979
|
-
rsubBytes
|
|
980
|
-
end
|
|
981
|
-
|
|
982
|
-
def aesEncrypt(block) #:nodoc:
|
|
983
|
-
@state = transpose(block)
|
|
984
|
-
expandedKey = expandKey(@key)
|
|
985
|
-
rounds = ROUNDS[@key.size]
|
|
986
|
-
|
|
987
|
-
aesMain(expandedKey, rounds)
|
|
988
|
-
end
|
|
989
|
-
|
|
990
|
-
def aesDecrypt(block) #:nodoc:
|
|
991
|
-
@state = transpose(block)
|
|
992
|
-
expandedKey = expandKey(@key)
|
|
993
|
-
rounds = ROUNDS[@key.size]
|
|
994
|
-
|
|
995
|
-
raesMain(expandedKey, rounds)
|
|
996
|
-
end
|
|
997
|
-
|
|
998
|
-
def aesMain(expandedKey, rounds) #:nodoc:
|
|
999
|
-
#puts "expandedKey: #{expandedKey.inspect}"
|
|
1000
|
-
roundKey = createRoundKey(expandedKey)
|
|
1001
|
-
addRoundKey(roundKey)
|
|
1002
|
-
|
|
1003
|
-
for i in 1..rounds-1
|
|
1004
|
-
roundKey = createRoundKey(expandedKey, i)
|
|
1005
|
-
aesRound(roundKey)
|
|
1006
|
-
end
|
|
905
|
+
#
|
|
906
|
+
# AES round methods.
|
|
907
|
+
#
|
|
1007
908
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
addRoundKey(roundKey)
|
|
1012
|
-
|
|
1013
|
-
transpose(@state)
|
|
1014
|
-
end
|
|
1015
|
-
|
|
1016
|
-
def raesMain(expandedKey, rounds) #:nodoc:
|
|
1017
|
-
|
|
1018
|
-
roundKey = createRoundKey(expandedKey, rounds)
|
|
1019
|
-
addRoundKey(roundKey)
|
|
1020
|
-
rshiftRows
|
|
1021
|
-
rsubBytes
|
|
1022
|
-
|
|
1023
|
-
(rounds - 1).downto(1) do |i|
|
|
1024
|
-
roundKey = createRoundKey(expandedKey, i)
|
|
1025
|
-
raesRound(roundKey)
|
|
1026
|
-
end
|
|
909
|
+
def create_round_key(expanded_key, round = 0) #:nodoc:
|
|
910
|
+
transpose(expanded_key[round * BLOCKSIZE, BLOCKSIZE])
|
|
911
|
+
end
|
|
1027
912
|
|
|
1028
|
-
|
|
1029
|
-
|
|
913
|
+
def add_round_key(roundKey) #:nodoc:
|
|
914
|
+
BLOCKSIZE.times do |i| @state[i] ^= roundKey[i] end
|
|
915
|
+
end
|
|
1030
916
|
|
|
1031
|
-
|
|
1032
|
-
|
|
917
|
+
def sub_bytes #:nodoc:
|
|
918
|
+
BLOCKSIZE.times do |i| @state[i] = SBOX[ @state[i] ] end
|
|
919
|
+
end
|
|
1033
920
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
# Class representing a crypt filter Dictionary
|
|
1038
|
-
#
|
|
1039
|
-
class CryptFilterDictionary < Dictionary
|
|
1040
|
-
include StandardObject
|
|
921
|
+
def r_sub_bytes #:nodoc:
|
|
922
|
+
BLOCKSIZE.times do |i| @state[i] = RSBOX[ @state[i] ] end
|
|
923
|
+
end
|
|
1041
924
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
925
|
+
def shift_rows #:nodoc:
|
|
926
|
+
NROWS.times do |i|
|
|
927
|
+
@state[i * NCOLS, NCOLS] = rol(@state[i * NCOLS, NCOLS], i)
|
|
928
|
+
end
|
|
929
|
+
end
|
|
1047
930
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
include StandardObject
|
|
1053
|
-
|
|
1054
|
-
field :Filter, :Type => Name, :Default => :Standard, :Required => true
|
|
1055
|
-
field :SubFilter, :Type => Name, :Version => "1.3"
|
|
1056
|
-
field :V, :Type => Integer, :Default => 0
|
|
1057
|
-
field :Length, :Type => Integer, :Default => 40, :Version => "1.4"
|
|
1058
|
-
field :CF, :Type => Dictionary, :Version => "1.5"
|
|
1059
|
-
field :StmF, :Type => Name, :Default => :Identity, :Version => "1.5"
|
|
1060
|
-
field :StrF, :Type => Name, :Default => :Identity, :Version => "1.5"
|
|
1061
|
-
field :EFF, :Type => Name, :Version => "1.6"
|
|
1062
|
-
end
|
|
1063
|
-
|
|
1064
|
-
#
|
|
1065
|
-
# The standard security handler for PDF encryption.
|
|
1066
|
-
#
|
|
1067
|
-
module Standard
|
|
1068
|
-
|
|
1069
|
-
PADDING = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A" #:nodoc:
|
|
1070
|
-
PADDING.force_encoding('binary') if RUBY_VERSION >= '1.9'
|
|
1071
|
-
|
|
1072
|
-
#
|
|
1073
|
-
# Permission constants for encrypted documents.
|
|
1074
|
-
#
|
|
1075
|
-
module Permissions
|
|
1076
|
-
RESERVED = 1 << 6 | 1 << 7 | 0xFFFFF000
|
|
1077
|
-
PRINT = 1 << 2 | RESERVED
|
|
1078
|
-
MODIFY_CONTENTS = 1 << 3 | RESERVED
|
|
1079
|
-
COPY_CONTENTS = 1 << 4 | RESERVED
|
|
1080
|
-
MODIFY_ANNOTATIONS = 1 << 5 | RESERVED
|
|
1081
|
-
FILLIN_FORMS = 1 << 8 | RESERVED
|
|
1082
|
-
EXTRACT_CONTENTS = 1 << 9 | RESERVED
|
|
1083
|
-
ASSEMBLE_DOC = 1 << 10 | RESERVED
|
|
1084
|
-
HIGH_QUALITY_PRINT = 1 << 11 | RESERVED
|
|
1085
|
-
|
|
1086
|
-
ALL = PRINT | MODIFY_CONTENTS | COPY_CONTENTS | MODIFY_ANNOTATIONS | FILLIN_FORMS | EXTRACT_CONTENTS | ASSEMBLE_DOC | HIGH_QUALITY_PRINT
|
|
1087
|
-
end
|
|
1088
|
-
|
|
1089
|
-
#
|
|
1090
|
-
# Class defining a standard encryption dictionary.
|
|
1091
|
-
#
|
|
1092
|
-
class Dictionary < EncryptionDictionary
|
|
1093
|
-
|
|
1094
|
-
field :R, :Type => Number, :Required => true
|
|
1095
|
-
field :O, :Type => String, :Required => true
|
|
1096
|
-
field :U, :Type => String, :Required => true
|
|
1097
|
-
field :OE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
|
|
1098
|
-
field :UE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
|
|
1099
|
-
field :Perms, :Type => String, :Version => '1.7', :ExtensionLevel => 3
|
|
1100
|
-
field :P, :Type => Integer, :Default => 0, :Required => true
|
|
1101
|
-
field :EncryptMetadata, :Type => Boolean, :Default => true, :Version => "1.5"
|
|
1102
|
-
|
|
1103
|
-
def pdf_version_required #:nodoc:
|
|
1104
|
-
if self.R > 5
|
|
1105
|
-
[ 1.7, 8 ]
|
|
1106
|
-
else
|
|
1107
|
-
super
|
|
1108
|
-
end
|
|
1109
|
-
end
|
|
1110
|
-
|
|
1111
|
-
#
|
|
1112
|
-
# Computes the key that will be used to encrypt/decrypt the document contents with user password.
|
|
1113
|
-
#
|
|
1114
|
-
def compute_user_encryption_key(userpassword, fileid)
|
|
1115
|
-
|
|
1116
|
-
if self.R < 5
|
|
1117
|
-
padded = pad_password(userpassword)
|
|
1118
|
-
padded.force_encoding('binary') if RUBY_VERSION >= '1.9'
|
|
1119
|
-
|
|
1120
|
-
padded << self.O
|
|
1121
|
-
padded << [ self.P ].pack("i")
|
|
1122
|
-
|
|
1123
|
-
padded << fileid
|
|
1124
|
-
|
|
1125
|
-
encrypt_metadata = self.EncryptMetadata != false
|
|
1126
|
-
padded << [ -1 ].pack("i") if self.R >= 4 and not encrypt_metadata
|
|
1127
|
-
|
|
1128
|
-
key = Digest::MD5.digest(padded)
|
|
1129
|
-
|
|
1130
|
-
50.times { key = Digest::MD5.digest(key[0, self.Length / 8]) } if self.R >= 3
|
|
1131
|
-
|
|
1132
|
-
if self.R == 2
|
|
1133
|
-
key[0, 5]
|
|
1134
|
-
elsif self.R >= 3
|
|
1135
|
-
key[0, self.Length / 8]
|
|
931
|
+
def r_shift_rows #:nodoc:
|
|
932
|
+
NROWS.times do |i|
|
|
933
|
+
@state[i * NCOLS, NCOLS] = ror(@state[i * NCOLS, NCOLS], i)
|
|
934
|
+
end
|
|
1136
935
|
end
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
936
|
+
|
|
937
|
+
def mix_column_with_field(column, field) #:nodoc:
|
|
938
|
+
p = field
|
|
939
|
+
|
|
940
|
+
column[0], column[1], column[2], column[3] =
|
|
941
|
+
galois_mult(column[0], p[0]) ^
|
|
942
|
+
galois_mult(column[3], p[1]) ^
|
|
943
|
+
galois_mult(column[2], p[2]) ^
|
|
944
|
+
galois_mult(column[1], p[3]),
|
|
945
|
+
|
|
946
|
+
galois_mult(column[1], p[0]) ^
|
|
947
|
+
galois_mult(column[0], p[1]) ^
|
|
948
|
+
galois_mult(column[3], p[2]) ^
|
|
949
|
+
galois_mult(column[2], p[3]),
|
|
950
|
+
|
|
951
|
+
galois_mult(column[2], p[0]) ^
|
|
952
|
+
galois_mult(column[1], p[1]) ^
|
|
953
|
+
galois_mult(column[0], p[2]) ^
|
|
954
|
+
galois_mult(column[3], p[3]),
|
|
955
|
+
|
|
956
|
+
galois_mult(column[3], p[0]) ^
|
|
957
|
+
galois_mult(column[2], p[1]) ^
|
|
958
|
+
galois_mult(column[1], p[2]) ^
|
|
959
|
+
galois_mult(column[0], p[3])
|
|
1146
960
|
end
|
|
1147
|
-
|
|
1148
|
-
iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
|
|
1149
|
-
AES.new(ukey, nil, false).decrypt(iv + self.UE.value)
|
|
1150
|
-
end
|
|
1151
|
-
end
|
|
1152
961
|
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
#
|
|
1157
|
-
def compute_owner_encryption_key(ownerpassword)
|
|
1158
|
-
if self.R >= 5
|
|
1159
|
-
passwd = password_to_utf8(ownerpassword)
|
|
962
|
+
def mix_column(column) #:nodoc:
|
|
963
|
+
mix_column_with_field(column, [ 2, 1, 1, 3 ])
|
|
964
|
+
end
|
|
1160
965
|
|
|
1161
|
-
|
|
966
|
+
def r_mix_column_(column) #:nodoc:
|
|
967
|
+
mix_column_with_field(column, [ 14, 9, 13, 11 ])
|
|
968
|
+
end
|
|
1162
969
|
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
970
|
+
def mix_columns #:nodoc:
|
|
971
|
+
NCOLS.times do |c|
|
|
972
|
+
column = []
|
|
973
|
+
NROWS.times do |r| column << @state[c + r * NCOLS] end
|
|
974
|
+
mix_column(column)
|
|
975
|
+
NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
|
|
976
|
+
end
|
|
1167
977
|
end
|
|
1168
978
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
979
|
+
def r_mix_columns #:nodoc:
|
|
980
|
+
NCOLS.times do |c|
|
|
981
|
+
column = []
|
|
982
|
+
NROWS.times do |r| column << @state[c + r * NCOLS] end
|
|
983
|
+
r_mix_column_(column)
|
|
984
|
+
NROWS.times do |r| @state[c + r * NCOLS] = column[r] end
|
|
985
|
+
end
|
|
986
|
+
end
|
|
1173
987
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
if self.R == 5
|
|
1197
|
-
self.U = Digest::SHA256.digest(upass + uvs) + uvs + uks
|
|
1198
|
-
self.O = Digest::SHA256.digest(opass + ovs + self.U) + ovs + oks
|
|
1199
|
-
ukey = Digest::SHA256.digest(upass + uks)
|
|
1200
|
-
okey = Digest::SHA256.digest(opass + oks + self.U)
|
|
1201
|
-
else
|
|
1202
|
-
self.U = compute_hardened_hash(upass, uvs) + uvs + uks
|
|
1203
|
-
self.O = compute_hardened_hash(opass, ovs, self.U) + ovs + oks
|
|
1204
|
-
ukey = compute_hardened_hash(upass, uks)
|
|
1205
|
-
okey = compute_hardened_hash(opass, oks, self.U)
|
|
988
|
+
def expand_key(key) #:nodoc:
|
|
989
|
+
key = key.unpack("C*")
|
|
990
|
+
size = key.size
|
|
991
|
+
expanded_size = 16 * (ROUNDS[key.size] + 1)
|
|
992
|
+
rcon_iter = 1
|
|
993
|
+
expanded_key = key[0, size]
|
|
994
|
+
|
|
995
|
+
while expanded_key.size < expanded_size
|
|
996
|
+
temp = expanded_key[-4, 4]
|
|
997
|
+
|
|
998
|
+
if expanded_key.size % size == 0
|
|
999
|
+
schedule_core(temp, rcon_iter)
|
|
1000
|
+
rcon_iter = rcon_iter.succ
|
|
1001
|
+
end
|
|
1002
|
+
|
|
1003
|
+
temp.map! do |b| SBOX[b] end if size == 32 and expanded_key.size % size == 16
|
|
1004
|
+
|
|
1005
|
+
temp.each do |b| expanded_key << (expanded_key[-size] ^ b) end
|
|
1006
|
+
end
|
|
1007
|
+
|
|
1008
|
+
expanded_key
|
|
1206
1009
|
end
|
|
1207
1010
|
|
|
1208
|
-
|
|
1209
|
-
|
|
1011
|
+
def aes_round(round_key) #:nodoc:
|
|
1012
|
+
sub_bytes
|
|
1013
|
+
#puts "after sub_bytes: #{@state.inspect}"
|
|
1014
|
+
shift_rows
|
|
1015
|
+
#puts "after shift_rows: #{@state.inspect}"
|
|
1016
|
+
mix_columns
|
|
1017
|
+
#puts "after mix_columns: #{@state.inspect}"
|
|
1018
|
+
add_round_key(round_key)
|
|
1019
|
+
#puts "roundKey = #{roundKey.inspect}"
|
|
1020
|
+
#puts "after add_round_key: #{@state.inspect}"
|
|
1021
|
+
end
|
|
1210
1022
|
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1023
|
+
def r_aes_round(round_key) #:nodoc:
|
|
1024
|
+
add_round_key(round_key)
|
|
1025
|
+
r_mix_columns
|
|
1026
|
+
r_shift_rows
|
|
1027
|
+
r_sub_bytes
|
|
1028
|
+
end
|
|
1217
1029
|
|
|
1218
|
-
|
|
1030
|
+
def aes_encrypt(block) #:nodoc:
|
|
1031
|
+
@state = transpose(block)
|
|
1032
|
+
expanded_key = expand_key(@key)
|
|
1033
|
+
rounds = ROUNDS[@key.size]
|
|
1219
1034
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
end
|
|
1223
|
-
|
|
1224
|
-
#
|
|
1225
|
-
# Checks user password.
|
|
1226
|
-
# For version 2,3 and 4, _salt_ is the document ID.
|
|
1227
|
-
# For version 5 and 6, _salt_ is the User Key Salt.
|
|
1228
|
-
#
|
|
1229
|
-
def is_user_password?(pass, salt)
|
|
1230
|
-
if self.R == 2
|
|
1231
|
-
compute_user_password(pass, salt) == self.U
|
|
1232
|
-
elsif self.R == 3 or self.R == 4
|
|
1233
|
-
compute_user_password(pass, salt)[0, 16] == self.U[0, 16]
|
|
1234
|
-
elsif self.R == 5
|
|
1235
|
-
uvs = self.U[32, 8]
|
|
1236
|
-
Digest::SHA256.digest(password_to_utf8(pass) + uvs) == self.U[0, 32]
|
|
1237
|
-
elsif self.R == 6
|
|
1238
|
-
uvs = self.U[32, 8]
|
|
1239
|
-
compute_hardened_hash(password_to_utf8(pass), uvs) == self.U[0, 32]
|
|
1240
|
-
end
|
|
1241
|
-
end
|
|
1242
|
-
|
|
1243
|
-
#
|
|
1244
|
-
# Checks owner password.
|
|
1245
|
-
# For version 2,3 and 4, _salt_ is the document ID.
|
|
1246
|
-
# For version 5, _salt_ is (Owner Key Salt + U)
|
|
1247
|
-
#
|
|
1248
|
-
def is_owner_password?(pass, salt)
|
|
1249
|
-
|
|
1250
|
-
if self.R < 5
|
|
1251
|
-
user_password = retrieve_user_password(pass)
|
|
1252
|
-
is_user_password?(user_password, salt)
|
|
1253
|
-
elsif self.R == 5
|
|
1254
|
-
ovs = self.O[32, 8]
|
|
1255
|
-
Digest::SHA256.digest(password_to_utf8(pass) + ovs + self.U) == self.O[0, 32]
|
|
1256
|
-
elsif self.R == 6
|
|
1257
|
-
ovs = self.O[32, 8]
|
|
1258
|
-
compute_hardened_hash(password_to_utf8(pass), ovs, self.U[0,48]) == self.O[0, 32]
|
|
1259
|
-
end
|
|
1260
|
-
end
|
|
1035
|
+
aes_main(expanded_key, rounds)
|
|
1036
|
+
end
|
|
1261
1037
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1038
|
+
def aes_decrypt(block) #:nodoc:
|
|
1039
|
+
@state = transpose(block)
|
|
1040
|
+
expanded_key = expand_key(@key)
|
|
1041
|
+
rounds = ROUNDS[@key.size]
|
|
1042
|
+
|
|
1043
|
+
r_aes_main(expanded_key, rounds)
|
|
1044
|
+
end
|
|
1045
|
+
|
|
1046
|
+
def aes_main(expanded_key, rounds) #:nodoc:
|
|
1047
|
+
#puts "expandedKey: #{expandedKey.inspect}"
|
|
1048
|
+
round_key = create_round_key(expanded_key)
|
|
1049
|
+
add_round_key(round_key)
|
|
1050
|
+
|
|
1051
|
+
for i in 1..rounds-1
|
|
1052
|
+
round_key = create_round_key(expanded_key, i)
|
|
1053
|
+
aes_round(round_key)
|
|
1054
|
+
end
|
|
1055
|
+
|
|
1056
|
+
round_key = create_round_key(expanded_key, rounds)
|
|
1057
|
+
sub_bytes
|
|
1058
|
+
shift_rows
|
|
1059
|
+
add_round_key(round_key)
|
|
1060
|
+
|
|
1061
|
+
transpose(@state)
|
|
1062
|
+
end
|
|
1063
|
+
|
|
1064
|
+
def r_aes_main(expanded_key, rounds) #:nodoc:
|
|
1065
|
+
round_key = create_round_key(expanded_key, rounds)
|
|
1066
|
+
add_round_key(round_key)
|
|
1067
|
+
r_shift_rows
|
|
1068
|
+
r_sub_bytes
|
|
1069
|
+
|
|
1070
|
+
(rounds - 1).downto(1) do |i|
|
|
1071
|
+
round_key = create_round_key(expanded_key, i)
|
|
1072
|
+
r_aes_round(round_key)
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
round_key = create_round_key(expanded_key)
|
|
1076
|
+
add_round_key(round_key)
|
|
1077
|
+
|
|
1078
|
+
transpose(@state)
|
|
1079
|
+
end
|
|
1277
1080
|
end
|
|
1278
|
-
|
|
1279
|
-
private
|
|
1280
1081
|
|
|
1281
1082
|
#
|
|
1282
|
-
#
|
|
1283
|
-
# Rev 2,3,4: O = crypt(user_pass, owner_key).
|
|
1284
|
-
# Rev 5: unused.
|
|
1083
|
+
# Class representing a crypt filter Dictionary
|
|
1285
1084
|
#
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
if self.R == 2
|
|
1294
|
-
hash[0, 5]
|
|
1295
|
-
elsif self.R >= 3
|
|
1296
|
-
hash[0, self.Length / 8]
|
|
1297
|
-
end
|
|
1085
|
+
class CryptFilterDictionary < Dictionary
|
|
1086
|
+
include StandardObject
|
|
1087
|
+
|
|
1088
|
+
field :Type, :Type => Name, :Default => :CryptFilter
|
|
1089
|
+
field :CFM, :Type => Name, :Default => :None
|
|
1090
|
+
field :AuthEvent, :Type => Name, :Default => :DocOpen
|
|
1091
|
+
field :Length, :Type => Integer
|
|
1298
1092
|
end
|
|
1299
|
-
|
|
1093
|
+
|
|
1300
1094
|
#
|
|
1301
|
-
#
|
|
1302
|
-
# Cannot be used with revision 5.
|
|
1095
|
+
# Common class for encryption dictionaries.
|
|
1303
1096
|
#
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
user_key = ARC4.encrypt(key, hash)
|
|
1316
|
-
|
|
1317
|
-
19.times { |i| user_key = ARC4.encrypt(xor(key,i+1), user_key) }
|
|
1318
|
-
|
|
1319
|
-
user_key.ljust(32, 0xFF.chr)
|
|
1320
|
-
end
|
|
1097
|
+
class EncryptionDictionary < Dictionary
|
|
1098
|
+
include StandardObject
|
|
1099
|
+
|
|
1100
|
+
field :Filter, :Type => Name, :Default => :Standard, :Required => true
|
|
1101
|
+
field :SubFilter, :Type => Name, :Version => "1.3"
|
|
1102
|
+
field :V, :Type => Integer, :Default => 0
|
|
1103
|
+
field :Length, :Type => Integer, :Default => 40, :Version => "1.4"
|
|
1104
|
+
field :CF, :Type => Dictionary, :Version => "1.5"
|
|
1105
|
+
field :StmF, :Type => Name, :Default => :Identity, :Version => "1.5"
|
|
1106
|
+
field :StrF, :Type => Name, :Default => :Identity, :Version => "1.5"
|
|
1107
|
+
field :EFF, :Type => Name, :Version => "1.6"
|
|
1321
1108
|
end
|
|
1322
1109
|
|
|
1323
1110
|
#
|
|
1324
|
-
#
|
|
1111
|
+
# The standard security handler for PDF encryption.
|
|
1325
1112
|
#
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1113
|
+
module Standard
|
|
1114
|
+
PADDING = "\x28\xBF\x4E\x5E\x4E\x75\x8A\x41\x64\x00\x4E\x56\xFF\xFA\x01\x08\x2E\x2E\x00\xB6\xD0\x68\x3E\x80\x2F\x0C\xA9\xFE\x64\x53\x69\x7A".b #:nodoc:
|
|
1115
|
+
|
|
1116
|
+
#
|
|
1117
|
+
# Permission constants for encrypted documents.
|
|
1118
|
+
#
|
|
1119
|
+
module Permissions
|
|
1120
|
+
RESERVED = 1 << 6 | 1 << 7 | 0xFFFFF000
|
|
1121
|
+
PRINT = 1 << 2 | RESERVED
|
|
1122
|
+
MODIFY_CONTENTS = 1 << 3 | RESERVED
|
|
1123
|
+
COPY_CONTENTS = 1 << 4 | RESERVED
|
|
1124
|
+
MODIFY_ANNOTATIONS = 1 << 5 | RESERVED
|
|
1125
|
+
FILLIN_FORMS = 1 << 8 | RESERVED
|
|
1126
|
+
EXTRACT_CONTENTS = 1 << 9 | RESERVED
|
|
1127
|
+
ASSEMBLE_DOC = 1 << 10 | RESERVED
|
|
1128
|
+
HIGH_QUALITY_PRINT = 1 << 11 | RESERVED
|
|
1129
|
+
|
|
1130
|
+
ALL = PRINT | MODIFY_CONTENTS | COPY_CONTENTS |
|
|
1131
|
+
MODIFY_ANNOTATIONS | FILLIN_FORMS | EXTRACT_CONTENTS |
|
|
1132
|
+
ASSEMBLE_DOC | HIGH_QUALITY_PRINT
|
|
1345
1133
|
end
|
|
1346
1134
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1135
|
+
#
|
|
1136
|
+
# Class defining a standard encryption dictionary.
|
|
1137
|
+
#
|
|
1138
|
+
class Dictionary < EncryptionDictionary
|
|
1139
|
+
|
|
1140
|
+
field :R, :Type => Number, :Required => true
|
|
1141
|
+
field :O, :Type => String, :Required => true
|
|
1142
|
+
field :U, :Type => String, :Required => true
|
|
1143
|
+
field :OE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
|
|
1144
|
+
field :UE, :Type => String, :Version => '1.7', :ExtensionLevel => 3
|
|
1145
|
+
field :Perms, :Type => String, :Version => '1.7', :ExtensionLevel => 3
|
|
1146
|
+
field :P, :Type => Integer, :Default => 0, :Required => true
|
|
1147
|
+
field :EncryptMetadata, :Type => Boolean, :Default => true, :Version => "1.5"
|
|
1148
|
+
|
|
1149
|
+
def version_required #:nodoc:
|
|
1150
|
+
if self.R > 5
|
|
1151
|
+
[ 1.7, 8 ]
|
|
1152
|
+
else
|
|
1153
|
+
super
|
|
1154
|
+
end
|
|
1155
|
+
end
|
|
1352
1156
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1157
|
+
#
|
|
1158
|
+
# Computes the key that will be used to encrypt/decrypt the document contents with user password.
|
|
1159
|
+
#
|
|
1160
|
+
def compute_user_encryption_key(userpassword, fileid)
|
|
1161
|
+
if self.R < 5
|
|
1162
|
+
padded = pad_password(userpassword)
|
|
1163
|
+
padded.force_encoding('binary')
|
|
1357
1164
|
|
|
1358
|
-
|
|
1359
|
-
|
|
1165
|
+
padded << self.O
|
|
1166
|
+
padded << [ self.P ].pack("i")
|
|
1360
1167
|
|
|
1361
|
-
|
|
1362
|
-
key = h[0, 16]
|
|
1363
|
-
input[0, block_size] = h[0, block_size]
|
|
1364
|
-
iv = h[16, 16]
|
|
1168
|
+
padded << fileid
|
|
1365
1169
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1170
|
+
encrypt_metadata = self.EncryptMetadata != false
|
|
1171
|
+
padded << [ -1 ].pack("i") if self.R >= 4 and not encrypt_metadata
|
|
1368
1172
|
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1173
|
+
key = Digest::MD5.digest(padded)
|
|
1174
|
+
|
|
1175
|
+
50.times { key = Digest::MD5.digest(key[0, self.Length / 8]) } if self.R >= 3
|
|
1176
|
+
|
|
1177
|
+
if self.R == 2
|
|
1178
|
+
key[0, 5]
|
|
1179
|
+
elsif self.R >= 3
|
|
1180
|
+
key[0, self.Length / 8]
|
|
1181
|
+
end
|
|
1182
|
+
else
|
|
1183
|
+
passwd = password_to_utf8(userpassword)
|
|
1380
1184
|
|
|
1381
|
-
|
|
1382
|
-
|
|
1185
|
+
uks = self.U[40, 8]
|
|
1186
|
+
|
|
1187
|
+
if self.R == 5
|
|
1188
|
+
ukey = Digest::SHA256.digest(passwd + uks)
|
|
1189
|
+
else
|
|
1190
|
+
ukey = compute_hardened_hash(passwd, uks)
|
|
1191
|
+
end
|
|
1192
|
+
|
|
1193
|
+
iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
|
|
1194
|
+
AES.new(ukey, nil, false).decrypt(iv + self.UE.value)
|
|
1195
|
+
end
|
|
1196
|
+
end
|
|
1197
|
+
|
|
1198
|
+
#
|
|
1199
|
+
# Computes the key that will be used to encrypt/decrypt the document contents with owner password.
|
|
1200
|
+
# Revision 5 and above.
|
|
1201
|
+
#
|
|
1202
|
+
def compute_owner_encryption_key(ownerpassword)
|
|
1203
|
+
if self.R >= 5
|
|
1204
|
+
passwd = password_to_utf8(ownerpassword)
|
|
1205
|
+
|
|
1206
|
+
oks = self.O[40, 8]
|
|
1207
|
+
|
|
1208
|
+
if self.R == 5
|
|
1209
|
+
okey = Digest::SHA256.digest(passwd + oks + self.U)
|
|
1210
|
+
else
|
|
1211
|
+
okey = compute_hardened_hash(passwd, oks, self.U)
|
|
1212
|
+
end
|
|
1213
|
+
|
|
1214
|
+
iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
|
|
1215
|
+
AES.new(okey, nil, false).decrypt(iv + self.OE.value)
|
|
1216
|
+
end
|
|
1217
|
+
end
|
|
1218
|
+
|
|
1219
|
+
#
|
|
1220
|
+
# Set up document passwords.
|
|
1221
|
+
#
|
|
1222
|
+
def set_passwords(ownerpassword, userpassword, salt = nil)
|
|
1223
|
+
if self.R < 5
|
|
1224
|
+
key = compute_owner_key(ownerpassword)
|
|
1225
|
+
upadded = pad_password(userpassword)
|
|
1226
|
+
|
|
1227
|
+
owner_key = RC4.encrypt(key, upadded)
|
|
1228
|
+
19.times { |i| owner_key = RC4.encrypt(xor(key,i+1), owner_key) } if self.R >= 3
|
|
1229
|
+
|
|
1230
|
+
self.O = owner_key
|
|
1231
|
+
self.U = compute_user_password(userpassword, salt)
|
|
1232
|
+
|
|
1233
|
+
else
|
|
1234
|
+
upass = password_to_utf8(userpassword)
|
|
1235
|
+
opass = password_to_utf8(ownerpassword)
|
|
1236
|
+
|
|
1237
|
+
uvs, uks, ovs, oks = ::Array.new(4) { Encryption.rand_bytes(8) }
|
|
1238
|
+
file_key = Encryption.strong_rand_bytes(32)
|
|
1239
|
+
iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
|
|
1240
|
+
|
|
1241
|
+
if self.R == 5
|
|
1242
|
+
self.U = Digest::SHA256.digest(upass + uvs) + uvs + uks
|
|
1243
|
+
self.O = Digest::SHA256.digest(opass + ovs + self.U) + ovs + oks
|
|
1244
|
+
ukey = Digest::SHA256.digest(upass + uks)
|
|
1245
|
+
okey = Digest::SHA256.digest(opass + oks + self.U)
|
|
1246
|
+
else
|
|
1247
|
+
self.U = compute_hardened_hash(upass, uvs) + uvs + uks
|
|
1248
|
+
self.O = compute_hardened_hash(opass, ovs, self.U) + ovs + oks
|
|
1249
|
+
ukey = compute_hardened_hash(upass, uks)
|
|
1250
|
+
okey = compute_hardened_hash(opass, oks, self.U)
|
|
1251
|
+
end
|
|
1252
|
+
|
|
1253
|
+
self.UE = AES.new(ukey, iv, false).encrypt(file_key)[iv.size, 32]
|
|
1254
|
+
self.OE = AES.new(okey, iv, false).encrypt(file_key)[iv.size, 32]
|
|
1255
|
+
|
|
1256
|
+
perms =
|
|
1257
|
+
[ self.P ].pack("V") + # 0-3
|
|
1258
|
+
[ -1 ].pack("V") + # 4-7
|
|
1259
|
+
(self.EncryptMetadata == true ? "T" : "F") + # 8
|
|
1260
|
+
"adb" + # 9-11
|
|
1261
|
+
[ 0 ].pack("V") # 12-15
|
|
1262
|
+
|
|
1263
|
+
self.Perms = AES.new(file_key, iv, false).encrypt(perms)[iv.size, 16]
|
|
1264
|
+
|
|
1265
|
+
file_key
|
|
1266
|
+
end
|
|
1267
|
+
end
|
|
1268
|
+
|
|
1269
|
+
#
|
|
1270
|
+
# Checks user password.
|
|
1271
|
+
# For version 2,3 and 4, _salt_ is the document ID.
|
|
1272
|
+
# For version 5 and 6, _salt_ is the User Key Salt.
|
|
1273
|
+
#
|
|
1274
|
+
def is_user_password?(pass, salt)
|
|
1275
|
+
|
|
1276
|
+
if self.R == 2
|
|
1277
|
+
compute_user_password(pass, salt) == self.U
|
|
1278
|
+
elsif self.R == 3 or self.R == 4
|
|
1279
|
+
compute_user_password(pass, salt)[0, 16] == self.U[0, 16]
|
|
1280
|
+
elsif self.R == 5
|
|
1281
|
+
uvs = self.U[32, 8]
|
|
1282
|
+
Digest::SHA256.digest(password_to_utf8(pass) + uvs) == self.U[0, 32]
|
|
1283
|
+
elsif self.R == 6
|
|
1284
|
+
uvs = self.U[32, 8]
|
|
1285
|
+
compute_hardened_hash(password_to_utf8(pass), uvs) == self.U[0, 32]
|
|
1286
|
+
end
|
|
1287
|
+
end
|
|
1288
|
+
|
|
1289
|
+
#
|
|
1290
|
+
# Checks owner password.
|
|
1291
|
+
# For version 2,3 and 4, _salt_ is the document ID.
|
|
1292
|
+
# For version 5, _salt_ is (Owner Key Salt + U)
|
|
1293
|
+
#
|
|
1294
|
+
def is_owner_password?(pass, salt)
|
|
1295
|
+
|
|
1296
|
+
if self.R < 5
|
|
1297
|
+
user_password = retrieve_user_password(pass)
|
|
1298
|
+
is_user_password?(user_password, salt)
|
|
1299
|
+
elsif self.R == 5
|
|
1300
|
+
ovs = self.O[32, 8]
|
|
1301
|
+
Digest::SHA256.digest(password_to_utf8(pass) + ovs + self.U) == self.O[0, 32]
|
|
1302
|
+
elsif self.R == 6
|
|
1303
|
+
ovs = self.O[32, 8]
|
|
1304
|
+
compute_hardened_hash(password_to_utf8(pass), ovs, self.U[0,48]) == self.O[0, 32]
|
|
1305
|
+
end
|
|
1306
|
+
end
|
|
1307
|
+
|
|
1308
|
+
#
|
|
1309
|
+
# Retrieve user password from owner password.
|
|
1310
|
+
# Cannot be used with revision 5.
|
|
1311
|
+
#
|
|
1312
|
+
def retrieve_user_password(ownerpassword)
|
|
1313
|
+
|
|
1314
|
+
key = compute_owner_key(ownerpassword)
|
|
1315
|
+
|
|
1316
|
+
if self.R == 2
|
|
1317
|
+
RC4.decrypt(key, self.O)
|
|
1318
|
+
elsif self.R == 3 or self.R == 4
|
|
1319
|
+
user_password = RC4.decrypt(xor(key, 19), self.O)
|
|
1320
|
+
19.times { |i| user_password = RC4.decrypt(xor(key, 18-i), user_password) }
|
|
1321
|
+
|
|
1322
|
+
user_password
|
|
1323
|
+
end
|
|
1324
|
+
end
|
|
1325
|
+
|
|
1326
|
+
private
|
|
1327
|
+
|
|
1328
|
+
#
|
|
1329
|
+
# Used to encrypt/decrypt the O field.
|
|
1330
|
+
# Rev 2,3,4: O = crypt(user_pass, owner_key).
|
|
1331
|
+
# Rev 5: unused.
|
|
1332
|
+
#
|
|
1333
|
+
def compute_owner_key(ownerpassword) #:nodoc:
|
|
1334
|
+
|
|
1335
|
+
opadded = pad_password(ownerpassword)
|
|
1336
|
+
|
|
1337
|
+
hash = Digest::MD5.digest(opadded)
|
|
1338
|
+
50.times { hash = Digest::MD5.digest(hash) } if self.R >= 3
|
|
1339
|
+
|
|
1340
|
+
if self.R == 2
|
|
1341
|
+
hash[0, 5]
|
|
1342
|
+
elsif self.R >= 3
|
|
1343
|
+
hash[0, self.Length / 8]
|
|
1344
|
+
end
|
|
1345
|
+
end
|
|
1346
|
+
|
|
1347
|
+
#
|
|
1348
|
+
# Compute the value of the U field.
|
|
1349
|
+
# Cannot be used with revision 5.
|
|
1350
|
+
#
|
|
1351
|
+
def compute_user_password(userpassword, salt) #:nodoc:
|
|
1352
|
+
|
|
1353
|
+
if self.R == 2
|
|
1354
|
+
key = compute_user_encryption_key(userpassword, salt)
|
|
1355
|
+
user_key = RC4.encrypt(key, PADDING)
|
|
1356
|
+
elsif self.R == 3 or self.R == 4
|
|
1357
|
+
key = compute_user_encryption_key(userpassword, salt)
|
|
1358
|
+
|
|
1359
|
+
upadded = PADDING + salt
|
|
1360
|
+
hash = Digest::MD5.digest(upadded)
|
|
1361
|
+
|
|
1362
|
+
user_key = RC4.encrypt(key, hash)
|
|
1363
|
+
|
|
1364
|
+
19.times { |i| user_key = RC4.encrypt(xor(key,i+1), user_key) }
|
|
1365
|
+
|
|
1366
|
+
user_key.ljust(32, 0xFF.chr)
|
|
1367
|
+
end
|
|
1368
|
+
end
|
|
1369
|
+
|
|
1370
|
+
#
|
|
1371
|
+
# Computes hardened hash used in revision 6 (extension level 8).
|
|
1372
|
+
#
|
|
1373
|
+
def compute_hardened_hash(password, salt, vector = '')
|
|
1374
|
+
block_size = 32
|
|
1375
|
+
input = Digest::SHA256.digest(password + salt + vector) + "\x00" * 32
|
|
1376
|
+
key = input[0, 16]
|
|
1377
|
+
iv = input[16, 16]
|
|
1378
|
+
digest, aes, h, x = nil, nil, nil, nil
|
|
1379
|
+
|
|
1380
|
+
i = 0
|
|
1381
|
+
while i < 64 or i < x[-1].ord + 32
|
|
1382
|
+
|
|
1383
|
+
block = input[0, block_size]
|
|
1384
|
+
|
|
1385
|
+
if Origami::OPTIONS[:use_openssl]
|
|
1386
|
+
aes = OpenSSL::Cipher::Cipher.new("aes-128-cbc").encrypt
|
|
1387
|
+
aes.iv = iv
|
|
1388
|
+
aes.key = key
|
|
1389
|
+
aes.padding = 0
|
|
1390
|
+
else
|
|
1391
|
+
fail "You need OpenSSL support to encrypt/decrypt documents with this method"
|
|
1392
|
+
end
|
|
1393
|
+
|
|
1394
|
+
64.times do |j|
|
|
1395
|
+
x = ''
|
|
1396
|
+
x += aes.update(password) unless password.empty?
|
|
1397
|
+
x += aes.update(block)
|
|
1398
|
+
x += aes.update(vector) unless vector.empty?
|
|
1399
|
+
|
|
1400
|
+
if j == 0
|
|
1401
|
+
block_size = 32 + (x.unpack("C16").inject(0) {|a,b| a+b} % 3) * 16
|
|
1402
|
+
digest = Digest::SHA2.new(block_size << 3)
|
|
1403
|
+
end
|
|
1404
|
+
|
|
1405
|
+
digest.update(x)
|
|
1406
|
+
end
|
|
1407
|
+
|
|
1408
|
+
h = digest.digest
|
|
1409
|
+
key = h[0, 16]
|
|
1410
|
+
input[0, block_size] = h[0, block_size]
|
|
1411
|
+
iv = h[16, 16]
|
|
1412
|
+
|
|
1413
|
+
i = i + 1
|
|
1414
|
+
end
|
|
1415
|
+
|
|
1416
|
+
h[0, 32]
|
|
1417
|
+
end
|
|
1418
|
+
|
|
1419
|
+
def xor(str, byte) #:nodoc:
|
|
1420
|
+
str.split(//).map!{|c| (c[0].ord ^ byte).chr }.join
|
|
1421
|
+
end
|
|
1422
|
+
|
|
1423
|
+
def pad_password(password) #:nodoc:
|
|
1424
|
+
return PADDING.dup if password.empty? # Fix for Ruby 1.9 bug
|
|
1425
|
+
password[0,32].ljust(32, PADDING)
|
|
1426
|
+
end
|
|
1427
|
+
|
|
1428
|
+
def password_to_utf8(passwd) #:nodoc:
|
|
1429
|
+
LiteralString.new(passwd).to_utf8[0, 127]
|
|
1430
|
+
end
|
|
1431
|
+
end
|
|
1383
1432
|
end
|
|
1384
|
-
|
|
1385
|
-
end
|
|
1386
|
-
|
|
1387
1433
|
end
|
|
1388
|
-
|
|
1389
|
-
end
|
|
1390
1434
|
|
|
1391
1435
|
end
|
|
1392
|
-
|
|
1393
|
-
__END__
|
|
1394
|
-
def hexprint(str)
|
|
1395
|
-
hex = ""
|
|
1396
|
-
str.each_byte do |b|
|
|
1397
|
-
digit = b.to_s(16)
|
|
1398
|
-
digit = "0" + digit if digit.size == 1
|
|
1399
|
-
hex << digit
|
|
1400
|
-
end
|
|
1401
|
-
|
|
1402
|
-
puts hex.upcase
|
|
1403
|
-
end
|
|
1404
|
-
|