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.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/README.md +112 -0
  4. data/bin/config/pdfcop.conf.yml +232 -233
  5. data/bin/gui/about.rb +27 -37
  6. data/bin/gui/config.rb +108 -117
  7. data/bin/gui/file.rb +416 -365
  8. data/bin/gui/gtkhex.rb +1138 -1153
  9. data/bin/gui/hexview.rb +55 -57
  10. data/bin/gui/imgview.rb +48 -51
  11. data/bin/gui/menu.rb +388 -386
  12. data/bin/gui/properties.rb +114 -130
  13. data/bin/gui/signing.rb +571 -617
  14. data/bin/gui/textview.rb +77 -95
  15. data/bin/gui/treeview.rb +382 -387
  16. data/bin/gui/walker.rb +227 -232
  17. data/bin/gui/xrefs.rb +56 -60
  18. data/bin/pdf2pdfa +53 -57
  19. data/bin/pdf2ruby +212 -228
  20. data/bin/pdfcop +338 -348
  21. data/bin/pdfdecompress +58 -65
  22. data/bin/pdfdecrypt +56 -60
  23. data/bin/pdfencrypt +75 -80
  24. data/bin/pdfexplode +185 -182
  25. data/bin/pdfextract +201 -218
  26. data/bin/pdfmetadata +83 -82
  27. data/bin/pdfsh +4 -5
  28. data/bin/pdfwalker +1 -2
  29. data/bin/shell/.irbrc +45 -82
  30. data/bin/shell/console.rb +105 -130
  31. data/bin/shell/hexdump.rb +40 -64
  32. data/examples/README.md +34 -0
  33. data/examples/attachments/attachment.rb +38 -0
  34. data/examples/attachments/nested_document.rb +51 -0
  35. data/examples/encryption/encryption.rb +28 -0
  36. data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
  37. data/examples/flash/flash.rb +37 -0
  38. data/{samples → examples}/flash/helloworld.swf +0 -0
  39. data/examples/forms/javascript.rb +54 -0
  40. data/examples/forms/xfa.rb +115 -0
  41. data/examples/javascript/hello_world.rb +22 -0
  42. data/examples/javascript/js_emulation.rb +54 -0
  43. data/examples/loop/goto.rb +32 -0
  44. data/examples/loop/named.rb +33 -0
  45. data/examples/signature/signature.rb +65 -0
  46. data/examples/uri/javascript.rb +56 -0
  47. data/examples/uri/open-uri.rb +21 -0
  48. data/examples/uri/submitform.rb +47 -0
  49. data/lib/origami.rb +29 -42
  50. data/lib/origami/3d.rb +350 -225
  51. data/lib/origami/acroform.rb +262 -288
  52. data/lib/origami/actions.rb +268 -288
  53. data/lib/origami/annotations.rb +697 -722
  54. data/lib/origami/array.rb +258 -184
  55. data/lib/origami/boolean.rb +74 -84
  56. data/lib/origami/catalog.rb +397 -434
  57. data/lib/origami/collections.rb +144 -0
  58. data/lib/origami/destinations.rb +233 -194
  59. data/lib/origami/dictionary.rb +253 -232
  60. data/lib/origami/encryption.rb +1274 -1243
  61. data/lib/origami/export.rb +232 -268
  62. data/lib/origami/extensions/fdf.rb +307 -220
  63. data/lib/origami/extensions/ppklite.rb +368 -435
  64. data/lib/origami/filespec.rb +197 -0
  65. data/lib/origami/filters.rb +301 -295
  66. data/lib/origami/filters/ascii.rb +177 -180
  67. data/lib/origami/filters/ccitt.rb +528 -535
  68. data/lib/origami/filters/crypt.rb +26 -35
  69. data/lib/origami/filters/dct.rb +46 -52
  70. data/lib/origami/filters/flate.rb +95 -94
  71. data/lib/origami/filters/jbig2.rb +49 -55
  72. data/lib/origami/filters/jpx.rb +38 -44
  73. data/lib/origami/filters/lzw.rb +189 -183
  74. data/lib/origami/filters/predictors.rb +221 -235
  75. data/lib/origami/filters/runlength.rb +103 -104
  76. data/lib/origami/font.rb +173 -186
  77. data/lib/origami/functions.rb +67 -81
  78. data/lib/origami/graphics.rb +25 -21
  79. data/lib/origami/graphics/colors.rb +178 -187
  80. data/lib/origami/graphics/instruction.rb +79 -85
  81. data/lib/origami/graphics/path.rb +142 -148
  82. data/lib/origami/graphics/patterns.rb +160 -167
  83. data/lib/origami/graphics/render.rb +43 -50
  84. data/lib/origami/graphics/state.rb +138 -153
  85. data/lib/origami/graphics/text.rb +188 -205
  86. data/lib/origami/graphics/xobject.rb +819 -815
  87. data/lib/origami/header.rb +63 -78
  88. data/lib/origami/javascript.rb +596 -597
  89. data/lib/origami/linearization.rb +285 -290
  90. data/lib/origami/metadata.rb +139 -148
  91. data/lib/origami/name.rb +112 -148
  92. data/lib/origami/null.rb +53 -62
  93. data/lib/origami/numeric.rb +162 -175
  94. data/lib/origami/obfuscation.rb +186 -174
  95. data/lib/origami/object.rb +593 -573
  96. data/lib/origami/outline.rb +42 -47
  97. data/lib/origami/outputintents.rb +73 -82
  98. data/lib/origami/page.rb +703 -592
  99. data/lib/origami/parser.rb +238 -290
  100. data/lib/origami/parsers/fdf.rb +41 -33
  101. data/lib/origami/parsers/pdf.rb +75 -95
  102. data/lib/origami/parsers/pdf/lazy.rb +137 -0
  103. data/lib/origami/parsers/pdf/linear.rb +64 -66
  104. data/lib/origami/parsers/ppklite.rb +34 -70
  105. data/lib/origami/pdf.rb +1030 -1005
  106. data/lib/origami/reference.rb +102 -102
  107. data/lib/origami/signature.rb +591 -609
  108. data/lib/origami/stream.rb +668 -551
  109. data/lib/origami/string.rb +397 -373
  110. data/lib/origami/template/patterns.rb +56 -0
  111. data/lib/origami/template/widgets.rb +151 -0
  112. data/lib/origami/trailer.rb +144 -158
  113. data/lib/origami/tree.rb +62 -0
  114. data/lib/origami/version.rb +23 -0
  115. data/lib/origami/webcapture.rb +88 -79
  116. data/lib/origami/xfa.rb +2863 -2882
  117. data/lib/origami/xreftable.rb +472 -384
  118. data/test/dataset/calc.pdf +85 -0
  119. data/test/dataset/crypto.pdf +82 -0
  120. data/test/dataset/empty.pdf +49 -0
  121. data/test/test_actions.rb +27 -0
  122. data/test/test_annotations.rb +90 -0
  123. data/test/test_pages.rb +31 -0
  124. data/test/test_pdf.rb +16 -0
  125. data/test/test_pdf_attachment.rb +34 -0
  126. data/test/test_pdf_create.rb +24 -0
  127. data/test/test_pdf_encrypt.rb +95 -0
  128. data/test/test_pdf_parse.rb +96 -0
  129. data/test/test_pdf_sign.rb +58 -0
  130. data/test/test_streams.rb +182 -0
  131. data/test/test_xrefs.rb +67 -0
  132. metadata +88 -58
  133. data/README +0 -67
  134. data/bin/pdf2graph +0 -121
  135. data/bin/pdfcocoon +0 -104
  136. data/lib/origami/file.rb +0 -233
  137. data/samples/README.txt +0 -45
  138. data/samples/actions/launch/calc.rb +0 -87
  139. data/samples/actions/launch/winparams.rb +0 -22
  140. data/samples/actions/loop/loopgoto.rb +0 -24
  141. data/samples/actions/loop/loopnamed.rb +0 -21
  142. data/samples/actions/named/named.rb +0 -31
  143. data/samples/actions/samba/smbrelay.rb +0 -26
  144. data/samples/actions/webbug/submitform.js +0 -26
  145. data/samples/actions/webbug/webbug-browser.rb +0 -68
  146. data/samples/actions/webbug/webbug-js.rb +0 -67
  147. data/samples/actions/webbug/webbug-reader.rb +0 -90
  148. data/samples/attachments/attach.rb +0 -40
  149. data/samples/attachments/attached.txt +0 -1
  150. data/samples/crypto/crypto.rb +0 -28
  151. data/samples/digsig/signed.rb +0 -46
  152. data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
  153. data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
  154. data/samples/exploits/exploit_customdictopen.rb +0 -55
  155. data/samples/exploits/getannots.rb +0 -69
  156. data/samples/flash/flash.rb +0 -31
  157. data/samples/javascript/attached.txt +0 -1
  158. data/samples/javascript/js.rb +0 -52
  159. data/templates/patterns.rb +0 -66
  160. data/templates/widgets.rb +0 -173
  161. data/templates/xdp.rb +0 -92
  162. data/test/ts_pdf.rb +0 -50
@@ -1,1404 +1,1435 @@
1
1
  =begin
2
2
 
3
- = File
4
- encryption.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume DelugrÈ <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- Origami is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU Lesser General Public License as published by
13
- the Free Software Foundation, either version 3 of the License, or
14
- (at your option) any later version.
15
-
16
- Origami is distributed in the hope that it will be useful,
17
- but WITHOUT ANY WARRANTY; without even the implied warranty of
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
- require 'openssl' if Origami::OPTIONS[:use_openssl]
22
+ require 'openssl' if Origami::OPTIONS[:use_openssl]
28
23
  rescue LoadError
29
- Origami::OPTIONS[:use_openssl] = false
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
- class EncryptionError < Exception #:nodoc:
38
- end
39
-
40
- class EncryptionInvalidPasswordError < EncryptionError #:nodoc:
41
- end
33
+ class EncryptionError < Error #:nodoc:
34
+ end
42
35
 
43
- class EncryptionNotSupportedError < EncryptionError #:nodoc:
44
- end
36
+ class EncryptionInvalidPasswordError < EncryptionError #:nodoc:
37
+ end
45
38
 
46
- class PDF
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
- # Decrypts the current document (only RC4 40..128 bits).
57
- # _passwd_:: The password to decrypt the document.
58
- #
59
- def decrypt(passwd = "")
60
-
61
- unless self.is_encrypted?
62
- raise EncryptionError, "PDF is not encrypted"
63
- end
64
-
65
- encrypt_dict = get_doc_attr(:Encrypt)
66
- handler = Encryption::Standard::Dictionary.new(encrypt_dict.dup)
67
-
68
- unless handler.Filter == :Standard
69
- raise EncryptionNotSupportedError, "Unknown security handler : '#{handler.Filter.to_s}'"
70
- end
71
-
72
- case handler.V.to_i
73
- when 1,2 then str_algo = stm_algo = Encryption::ARC4
74
- when 4,5
75
- if handler[:CF].is_a?(Dictionary)
76
- cfs = handler[:CF]
77
-
78
- if handler[:StrF].is_a?(Name) and cfs[handler[:StrF]].is_a?(Dictionary)
79
- cfdict = cfs[handler[:StrF]]
80
-
81
- str_algo =
82
- if cfdict[:CFM] == :V2 then Encryption::ARC4
83
- elsif cfdict[:CFM] == :AESV2 then Encryption::AES
84
- elsif cfdict[:CFM] == :None then Encryption::Identity
85
- elsif cfdict[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
86
- else
87
- raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
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
- str_algo = Encryption::Identity
104
+ raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
91
105
  end
92
106
 
93
- if handler[:StmF].is_a?(Name) and cfs[handler[:StmF]].is_a?(Dictionary)
94
- cfdict = cfs[handler[:StmF]]
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
- stm_algo =
97
- if cfdict[:CFM] == :V2 then Encryption::ARC4
98
- elsif cfdict[:CFM] == :AESV2 then Encryption::AES
99
- elsif cfdict[:CFM] == :None then Encryption::Identity
100
- elsif cfdict[:CFM] == :AESV3 and handler.V.to_i == 5 then Encryption::AES
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
- raise EncryptionNotSupportedError, "Unsupported encryption version : #{handler.V}"
121
+ encryption_key = handler.compute_owner_encryption_key(passwd)
103
122
  end
104
123
  else
105
- stm_algo = Encryption::Identity
124
+ raise EncryptionInvalidPasswordError
106
125
  end
107
126
 
108
- else
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
- encrypted_objects.each do |obj|
162
-
163
- case obj
164
- when String
165
- next if obj.equal?(encrypt_dict[:U]) or
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
- self
189
- end
190
-
191
- #
192
- # Encrypts the current document with the provided passwords.
193
- # The document will be encrypted at writing-on-disk time.
194
- # _userpasswd_:: The user password.
195
- # _ownerpasswd_:: The owner password.
196
- # _options_:: A set of options to configure encryption.
197
- #
198
- def encrypt(options = {})
199
-
200
- if self.is_encrypted?
201
- raise EncryptionError, "PDF is already encrypted"
202
- end
203
-
204
- #
205
- # Default encryption options.
206
- #
207
- params =
208
- {
209
- :user_passwd => '',
210
- :owner_passwd => '',
211
- :cipher => 'rc4', # :RC4 or :AES
212
- :key_size => 128, # Key size in bits
213
- :hardened => false, # Use newer password validation (since Reader X)
214
- :encrypt_metadata => true, # Metadata shall be encrypted?
215
- :permissions => Encryption::Standard::Permissions::ALL # Document permissions
216
- }.update(options)
217
-
218
- userpasswd, ownerpasswd = params[:user_passwd], params[:owner_passwd]
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
- else
232
- raise EncryptionError, "Invalid RC4 key length"
233
- end
234
- when 'AES'
235
- algorithm = Encryption::AES
236
- if params[:key_size] == 128
237
- version = revision = 4
238
- elsif params[:key_size] == 256
239
- version = 5
240
- if params[:hardened]
241
- revision = 6
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
- revision = 5
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
- cryptfilter.Length = params[:key_size] >> 3
241
+ doc_id = (trailer_key(:ID) || generate_id).first
274
242
 
275
- handler.CF[:StdCF] = cryptfilter
276
- handler.StmF = handler.StrF = :StdCF
277
- end
278
-
279
- handler.set_passwords(ownerpasswd, userpasswd, doc_id)
280
- encryption_key = handler.compute_user_encryption_key(userpasswd, doc_id)
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
- fileInfo = get_trailer_info
283
- fileInfo[:Encrypt] = self << handler
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
- self.extend(Encryption::EncryptedDocument)
286
- self.encryption_dict = handler
287
- self.encryption_key = encryption_key
288
- self.stm_algo = self.str_algo = algorithm
256
+ if revision == 4
257
+ cryptfilter.CFM = :AESV2
258
+ else
259
+ cryptfilter.CFM = :AESV3
260
+ end
289
261
 
290
- self
291
- end
292
-
293
- end
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
- # Module to provide support for encrypting and decrypting PDF documents.
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
- # Generates _n_ random bytes from a fast PRNG.
302
- #
303
- def self.rand_bytes(n)
304
- ::Array.new(n) { rand(256) }.pack("C*")
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
- # Generates _n_ random bytes from a crypto PRNG.
285
+ # Module to provide support for encrypting and decrypting PDF documents.
309
286
  #
310
- def self.strong_rand_bytes(n)
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
- attr_writer :encryption_key
323
- attr_writer :encryption_dict
324
- attr_writer :stm_algo
325
- attr_writer :str_algo
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
- private
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
- def physicalize(options = {})
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
- def build(obj, revision, options) #:nodoc:
332
- if obj.is_a?(EncryptedObject) # already built
333
- if options[:decrypt] == true
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
- return
341
- end
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
- end
348
-
349
- obj.pre_build
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
- when Stream
369
- return if obj.is_a?(XRefStream)
370
- return if obj.equal?(self.Catalog.Metadata) and not @encryption_dict.EncryptMetadata
371
- obj.extend(EncryptedStream)
372
- obj.decrypted = true
373
- obj.encryption_handler = @encryption_dict
374
- obj.encryption_key = @encryption_key
375
- obj.algorithm = @stm_algo
376
-
377
- when Dictionary, Array
378
- obj.map! do |subobj|
379
- if subobj.is_indirect?
380
- if get_object(subobj.reference)
381
- subobj.reference
382
- else
383
- ref = add_to_revision(subobj, revision)
384
- build(subobj, revision, options)
385
- ref
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
- else
388
- subobj
389
- end
411
+
412
+ self
390
413
  end
391
-
392
- obj.each do |subobj|
393
- build(subobj, revision, options)
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
- obj.post_build
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
- # remove encrypt dictionary if requested
406
- if options[:decrypt]
407
- delete_object(get_trailer_info[:Encrypt])
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
- end
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
- # Module for encrypted String.
457
- #
458
- module EncryptedString
459
- include EncryptedObject
460
-
461
- def encrypt!
462
- if @decrypted
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
- self
490
- end
491
- end
451
+ #
452
+ # Module for encrypted String.
453
+ #
454
+ module EncryptedString
455
+ include EncryptedObject
492
456
 
493
- #
494
- # Module for encrypted Stream.
495
- #
496
- module EncryptedStream
497
- include EncryptedObject
457
+ def self.extended(obj)
458
+ obj.decrypted = false
459
+ end
498
460
 
499
- def encrypt!
500
- if @decrypted
501
- encode!
461
+ def encrypt!
462
+ return self unless @decrypted
502
463
 
503
- key = compute_object_key
464
+ cipher = self.document.string_encryption_cipher
465
+ raise EncryptionError, "Cannot find string encryption filter" if cipher.nil?
504
466
 
505
- @rawdata =
506
- if @algorithm == ARC4 or @algorithm == Identity
507
- @algorithm.encrypt(key, self.rawdata)
508
- else
509
- iv = Encryption.rand_bytes(AES::BLOCKSIZE)
510
- @algorithm.encrypt(key, iv, @rawdata)
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
- @decrypted = false
485
+ def decrypt!
486
+ return self if @decrypted
514
487
 
515
- @rawdata.freeze
516
- self.freeze
517
- end
488
+ cipher = self.document.string_encryption_cipher
489
+ raise EncryptionError, "Cannot find string encryption filter" if cipher.nil?
518
490
 
519
- self
520
- end
491
+ key = compute_object_key(cipher)
521
492
 
522
- def decrypt!
523
- unless @decrypted
524
- key = compute_object_key
493
+ self.replace(cipher.decrypt(key, self.to_str))
494
+ @decrypted = true
525
495
 
526
- self.rawdata = @algorithm.decrypt(key, @rawdata)
527
- @decrypted = true
496
+ self
497
+ end
528
498
  end
529
499
 
530
- self
531
- end
500
+ #
501
+ # Module for encrypted Stream.
502
+ #
503
+ module EncryptedStream
504
+ include EncryptedObject
532
505
 
533
- end
506
+ def self.extended(obj)
507
+ obj.decrypted = false
508
+ end
534
509
 
535
- #
536
- # Identity transformation.
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
- end
515
+ if self.filters.first == :Crypt
516
+ params = decode_params.first
625
517
 
626
- #
627
- # Pure Ruby implementation of the AES symmetric algorithm.
628
- # Using mode CBC.
629
- #
630
- class AES
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
- if not iv.nil? and iv.size != BLOCKSIZE
725
- raise EncryptionError, "Initialization vector must have a length of #{BLOCKSIZE} bytes."
726
- end
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
- @key = key
729
- @iv = iv
730
- @use_padding = use_padding
731
- end
530
+ key = compute_object_key(cipher)
732
531
 
733
- def encrypt(data)
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
- if @iv.nil?
736
- raise EncryptionError, "No initialization vector has been set."
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
- if @use_padding
740
- padlen = BLOCKSIZE - (data.size % BLOCKSIZE)
741
- data << (padlen.chr * padlen)
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
- if Origami::OPTIONS[:use_openssl]
745
- aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").encrypt
746
- aes.iv = @iv
747
- aes.key = @key
748
- aes.padding = 0
588
+ #
589
+ # Pure Ruby implementation of the RC4 symmetric algorithm
590
+ #
591
+ class RC4
749
592
 
750
- @iv + aes.update(data) + aes.final
751
- else
752
- cipher = []
753
- cipherblock = []
754
- nblocks = data.size / BLOCKSIZE
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
- first_round = true
757
- nblocks.times do |n|
758
- plainblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
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
- if first_round
761
- BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
762
- else
763
- BLOCKSIZE.times do |i| plainblock[i] ^= cipherblock[i] end
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
- first_round = false
767
- cipherblock = aesEncrypt(plainblock)
768
- cipher.concat(cipherblock)
769
- end
618
+ #
619
+ # Encrypt/decrypt data with the RC4 encryption algorithm
620
+ #
621
+ def cipher(data)
622
+ return "" if data.empty?
770
623
 
771
- @iv + cipher.pack("C*")
772
- end
773
- end
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
- def decrypt(data)
776
- unless data.size % BLOCKSIZE == 0
777
- raise EncryptionError,
778
- "Data must be 16-bytes padded (data size = #{data.size} bytes)"
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
- @iv = data.slice!(0, BLOCKSIZE)
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
- if Origami::OPTIONS[:use_openssl]
784
- aes = OpenSSL::Cipher::Cipher.new("aes-#{@key.length << 3}-cbc").decrypt
785
- aes.iv = @iv
786
- aes.key = @key
787
- aes.padding = 0
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
- plain = (aes.update(data) + aes.final).unpack("C*")
790
- else
791
- plain = []
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
- first_round = true
797
- nblocks.times do |n|
798
- cipherblock = data[n * BLOCKSIZE, BLOCKSIZE].unpack("C*")
765
+ @key = key
766
+ @iv = iv
767
+ @use_padding = use_padding
768
+ end
799
769
 
800
- plainblock = aesDecrypt(cipherblock)
770
+ def encrypt(data)
771
+ if @iv.nil?
772
+ raise EncryptionError, "No initialization vector has been set."
773
+ end
801
774
 
802
- if first_round
803
- BLOCKSIZE.times do |i| plainblock[i] ^= @iv[i].ord end
804
- else
805
- BLOCKSIZE.times do |i| plainblock[i] ^= prev_cipherblock[i] end
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
- first_round = false
809
- prev_cipherblock = cipherblock
810
- plain.concat(plainblock)
811
- end
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
- if @use_padding
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
- plain.pack("C*")
828
- end
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
- private
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
- def rol(row, n = 1) #:nodoc
833
- n.times do row.push row.shift end ; row
834
- end
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
- def ror(row, n = 1) #:nodoc:
837
- n.times do row.unshift row.pop end ; row
838
- end
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
- def galoisMult(a, b) #:nodoc:
841
- p = 0
861
+ plain.pack("C*")
862
+ end
842
863
 
843
- 8.times do
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
- p % 256
852
- end
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
- def rshiftRows #:nodoc:
898
- NROWS.times do |i|
899
- @state[i * NCOLS, NCOLS] = ror(@state[i * NCOLS, NCOLS], i)
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
- def expandKey(key) #:nodoc:
874
+ def galois_mult(a, b) #:nodoc:
875
+ p = 0
940
876
 
941
- key = key.unpack("C*")
942
- size = key.size
943
- expandedSize = 16 * (ROUNDS[key.size] + 1)
944
- rconIter = 1
945
- expandedKey = key[0, size]
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
- while expandedKey.size < expandedSize
948
- temp = expandedKey[-4, 4]
885
+ p % 256
886
+ end
949
887
 
950
- if expandedKey.size % size == 0
951
- scheduleCore(temp, rconIter)
952
- rconIter = rconIter.succ
953
- end
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
- temp.map! do |b| SBOX[b] end if size == 32 and expandedKey.size % size == 16
893
+ word
894
+ end
956
895
 
957
- temp.each do |b| expandedKey << (expandedKey[-size] ^ b) end
958
- end
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
- expandedKey
961
- end
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
- roundKey = createRoundKey(expandedKey, rounds)
1009
- subBytes
1010
- shiftRows
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
- roundKey = createRoundKey(expandedKey)
1029
- addRoundKey(roundKey)
913
+ def add_round_key(roundKey) #:nodoc:
914
+ BLOCKSIZE.times do |i| @state[i] ^= roundKey[i] end
915
+ end
1030
916
 
1031
- transpose(@state)
1032
- end
917
+ def sub_bytes #:nodoc:
918
+ BLOCKSIZE.times do |i| @state[i] = SBOX[ @state[i] ] end
919
+ end
1033
920
 
1034
- end
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
- field :Type, :Type => Name, :Default => :CryptFilter
1043
- field :CFM, :Type => Name, :Default => :None
1044
- field :AuthEvent, :Type => Name, :Default => :DocOpen
1045
- field :Length, :Type => Integer
1046
- end
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
- # Common class for encryption dictionaries.
1050
- #
1051
- class EncryptionDictionary < Dictionary
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
- else
1138
- passwd = password_to_utf8(userpassword)
1139
-
1140
- uks = self.U[40, 8]
1141
-
1142
- if self.R == 5
1143
- ukey = Digest::SHA256.digest(passwd + uks)
1144
- else
1145
- ukey = compute_hardened_hash(passwd, uks)
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
- # Computes the key that will be used to encrypt/decrypt the document contents with owner password.
1155
- # Revision 5 and above.
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
- oks = self.O[40, 8]
966
+ def r_mix_column_(column) #:nodoc:
967
+ mix_column_with_field(column, [ 14, 9, 13, 11 ])
968
+ end
1162
969
 
1163
- if self.R == 5
1164
- okey = Digest::SHA256.digest(passwd + oks + self.U)
1165
- else
1166
- okey = compute_hardened_hash(passwd, oks, self.U)
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
- iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1170
- AES.new(okey, nil, false).decrypt(iv + self.OE.value)
1171
- end
1172
- end
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
- # Set up document passwords.
1176
- #
1177
- def set_passwords(ownerpassword, userpassword, salt = nil)
1178
- if self.R < 5
1179
- key = compute_owner_key(ownerpassword)
1180
- upadded = pad_password(userpassword)
1181
-
1182
- owner_key = ARC4.encrypt(key, upadded)
1183
- 19.times { |i| owner_key = ARC4.encrypt(xor(key,i+1), owner_key) } if self.R >= 3
1184
-
1185
- self.O = owner_key
1186
- self.U = compute_user_password(userpassword, salt)
1187
-
1188
- else
1189
- upass = password_to_utf8(userpassword)
1190
- opass = password_to_utf8(ownerpassword)
1191
-
1192
- uvs, uks, ovs, oks = ::Array.new(4) { Encryption.rand_bytes(8) }
1193
- file_key = Encryption.strong_rand_bytes(32)
1194
- iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
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
- self.UE = AES.new(ukey, iv, false).encrypt(file_key)[iv.size, 32]
1209
- self.OE = AES.new(okey, iv, false).encrypt(file_key)[iv.size, 32]
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
- perms =
1212
- [ self.P ].pack("V") + # 0-3
1213
- [ -1 ].pack("V") + # 4-7
1214
- (self.EncryptMetadata == true ? "T" : "F") + # 8
1215
- "adb" + # 9-11
1216
- [ 0 ].pack("V") # 12-15
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
- self.Perms = AES.new(file_key, iv, false).encrypt(perms)[iv.size, 16]
1030
+ def aes_encrypt(block) #:nodoc:
1031
+ @state = transpose(block)
1032
+ expanded_key = expand_key(@key)
1033
+ rounds = ROUNDS[@key.size]
1219
1034
 
1220
- file_key
1221
- end
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
- # Retrieve user password from owner password.
1264
- # Cannot be used with revision 5.
1265
- #
1266
- def retrieve_user_password(ownerpassword)
1267
- key = compute_owner_key(ownerpassword)
1268
-
1269
- if self.R == 2
1270
- ARC4.decrypt(key, self.O)
1271
- elsif self.R == 3 or self.R == 4
1272
- user_password = ARC4.decrypt(xor(key, 19), self.O)
1273
- 19.times { |i| user_password = ARC4.decrypt(xor(key, 18-i), user_password) }
1274
-
1275
- user_password
1276
- end
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
- # Used to encrypt/decrypt the O field.
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
- def compute_owner_key(ownerpassword) #:nodoc:
1287
-
1288
- opadded = pad_password(ownerpassword)
1289
-
1290
- hash = Digest::MD5.digest(opadded)
1291
- 50.times { hash = Digest::MD5.digest(hash) } if self.R >= 3
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
- # Compute the value of the U field.
1302
- # Cannot be used with revision 5.
1095
+ # Common class for encryption dictionaries.
1303
1096
  #
1304
- def compute_user_password(userpassword, salt) #:nodoc:
1305
-
1306
- if self.R == 2
1307
- key = compute_user_encryption_key(userpassword, salt)
1308
- user_key = ARC4.encrypt(key, PADDING)
1309
- elsif self.R == 3 or self.R == 4
1310
- key = compute_user_encryption_key(userpassword, salt)
1311
-
1312
- upadded = PADDING + salt
1313
- hash = Digest::MD5.digest(upadded)
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
- # Computes hardened hash used in revision 6 (extension level 8).
1111
+ # The standard security handler for PDF encryption.
1325
1112
  #
1326
- def compute_hardened_hash(password, salt, vector = '')
1327
- block_size = 32
1328
- input = Digest::SHA256.digest(password + salt + vector) + "\x00" * 32
1329
- key = input[0, 16]
1330
- iv = input[16, 16]
1331
- digest, aes, h, x = nil, nil, nil, nil
1332
-
1333
- i = 0
1334
- while i < 64 or i < x[-1].ord + 32
1335
- j = 0
1336
- block = input[0, block_size]
1337
-
1338
- if Origami::OPTIONS[:use_openssl]
1339
- aes = OpenSSL::Cipher::Cipher.new("aes-128-cbc").encrypt
1340
- aes.iv = iv
1341
- aes.key = key
1342
- aes.padding = 0
1343
- else
1344
- fail "You need OpenSSL support to encrypt/decrypt documents with this method"
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
- 64.times do |j|
1348
- x = ''
1349
- x += aes.update(password) unless password.empty?
1350
- x += aes.update(block)
1351
- x += aes.update(vector) unless vector.empty?
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
- if j == 0
1354
- block_size = 32 + (x.unpack("C16").inject(0) {|a,b| a+b} % 3) * 16
1355
- digest = Digest::SHA2.new(block_size << 3)
1356
- end
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
- digest.update(x)
1359
- end
1165
+ padded << self.O
1166
+ padded << [ self.P ].pack("i")
1360
1167
 
1361
- h = digest.digest
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
- i = i + 1
1367
- end
1170
+ encrypt_metadata = self.EncryptMetadata != false
1171
+ padded << [ -1 ].pack("i") if self.R >= 4 and not encrypt_metadata
1368
1172
 
1369
- h[0, 32]
1370
- end
1371
-
1372
- def xor(str, byte) #:nodoc:
1373
- str.split(//).map!{|c| (c[0].ord ^ byte).chr }.join
1374
- end
1375
-
1376
- def pad_password(password) #:nodoc:
1377
- return PADDING.dup if password.empty? # Fix for Ruby 1.9 bug
1378
- password[0,32].ljust(32, PADDING)
1379
- end
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
- def password_to_utf8(passwd) #:nodoc:
1382
- Origami::ByteString.new(passwd).to_utf8[0, 127]
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
-