origami 1.2.5 → 1.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/bin/gui/config.rb +0 -4
  3. data/bin/gui/imgview.rb +2 -2
  4. data/bin/gui/menu.rb +11 -3
  5. data/bin/gui/treeview.rb +9 -3
  6. data/bin/pdfexplode +220 -0
  7. data/bin/pdfextract +3 -0
  8. data/lib/origami/acroform.rb +2 -2
  9. data/lib/origami/actions.rb +62 -35
  10. data/lib/origami/annotations.rb +3 -2
  11. data/lib/origami/array.rb +27 -4
  12. data/lib/origami/boolean.rb +2 -2
  13. data/lib/origami/catalog.rb +45 -45
  14. data/lib/origami/dictionary.rb +87 -14
  15. data/lib/origami/encryption.rb +46 -24
  16. data/lib/origami/file.rb +1 -2
  17. data/lib/origami/filters/ccitt.rb +118 -66
  18. data/lib/origami/filters/flate.rb +5 -1
  19. data/lib/origami/filters.rb +84 -2
  20. data/lib/origami/font.rb +71 -71
  21. data/lib/origami/graphics/patterns.rb +2 -1
  22. data/lib/origami/graphics/xobject.rb +123 -1
  23. data/lib/origami/javascript.rb +2 -1
  24. data/lib/origami/name.rb +2 -2
  25. data/lib/origami/null.rb +2 -2
  26. data/lib/origami/numeric.rb +11 -3
  27. data/lib/origami/object.rb +37 -16
  28. data/lib/origami/page.rb +135 -71
  29. data/lib/origami/parser.rb +11 -4
  30. data/lib/origami/parsers/pdf/linear.rb +1 -0
  31. data/lib/origami/parsers/pdf.rb +10 -0
  32. data/lib/origami/pdf.rb +10 -70
  33. data/lib/origami/reference.rb +4 -5
  34. data/lib/origami/signature.rb +22 -8
  35. data/lib/origami/stream.rb +41 -20
  36. data/lib/origami/string.rb +15 -6
  37. data/lib/origami/trailer.rb +9 -5
  38. data/lib/origami.rb +19 -0
  39. data/samples/actions/loop/loopgoto.rb +1 -1
  40. data/samples/actions/loop/loopnamed.rb +2 -2
  41. data/samples/actions/named/named.rb +1 -1
  42. data/samples/actions/samba/smbrelay.rb +1 -1
  43. data/samples/actions/triggerevents/trigger.rb +13 -13
  44. data/samples/actions/webbug/webbug-browser.rb +1 -1
  45. data/samples/actions/webbug/webbug-js.rb +1 -1
  46. data/samples/actions/webbug/webbug-reader.rb +1 -1
  47. data/samples/attachments/attach.rb +2 -2
  48. data/samples/exploits/cve-2008-2992-utilprintf.rb +1 -1
  49. data/samples/exploits/cve-2009-0927-geticon.rb +1 -1
  50. data/samples/exploits/exploit_customdictopen.rb +2 -2
  51. data/samples/exploits/getannots.rb +1 -1
  52. data/samples/javascript/js.rb +2 -2
  53. data/test/ts_pdf.rb +23 -23
  54. metadata +71 -86
@@ -284,51 +284,6 @@ module Origami
284
284
  ATTACHMENTS = :UseAttachments
285
285
  end
286
286
 
287
- #
288
- # Class representing the Catalog Dictionary of a PDF file.
289
- #
290
- class Catalog < Dictionary
291
-
292
- include StandardObject
293
-
294
- field :Type, :Type => Name, :Default => :Catalog, :Required => true
295
- field :Version, :Type => Name, :Version => "1.4"
296
- field :Pages, :Type => Dictionary, :Required => true
297
- field :PageLabels, :Type => Dictionary, :Version => "1.3"
298
- field :Names, :Type => Dictionary, :Version => "1.2"
299
- field :Dests, :Type => Dictionary, :Version => "1.1"
300
- field :ViewerPreferences, :Type => Dictionary, :Version => "1.2"
301
- field :PageLayout, :Type => Name, :Default => PageLayout::SINGLE
302
- field :PageMode, :Type => Name, :Default => PageMode::NONE
303
- field :Outlines, :Type => Dictionary
304
- field :Threads, :Type => Array, :Version => "1.1"
305
- field :OpenAction, :Type => [ Array, Dictionary ], :Version => "1.1"
306
- field :AA, :Type => Dictionary, :Version => "1.4"
307
- field :URI, :Type => Dictionary, :Version => "1.1"
308
- field :AcroForm, :Type => Dictionary, :Version => "1.2"
309
- field :Metadata, :Type => Stream, :Version => "1.4"
310
- field :StructTreeRoot, :Type => Dictionary, :Version => "1.3"
311
- field :MarkInfo, :Type => Dictionary, :Version => "1.4"
312
- field :Lang, :Type => String, :Version => "1.4"
313
- field :SpiderInfo, :Type => Dictionary, :Version => "1.3"
314
- field :OutputIntents, :Type => Array, :Version => "1.4"
315
- field :PieceInfo, :Type => Dictionary, :Version => "1.4"
316
- field :OCProperties, :Type => Dictionary, :Version => "1.5"
317
- field :Perms, :Type => Dictionary, :Version => "1.5"
318
- field :Legal, :Type => Dictionary, :Version => "1.5"
319
- field :Requirements, :Type => Array, :Version => "1.7"
320
- field :Collection, :Type => Dictionary, :Version => "1.7"
321
- field :NeedsRendering, :Type => Boolean, :Version => "1.7", :Default => false
322
- field :Extensions, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
323
-
324
- def initialize(hash = {})
325
- set_indirect(true)
326
-
327
- super(hash)
328
- end
329
-
330
- end
331
-
332
287
  #
333
288
  # Class representing additional actions which can be associated with a Catalog.
334
289
  #
@@ -482,4 +437,49 @@ module Origami
482
437
 
483
438
  end
484
439
 
440
+ #
441
+ # Class representing the Catalog Dictionary of a PDF file.
442
+ #
443
+ class Catalog < Dictionary
444
+
445
+ include StandardObject
446
+
447
+ field :Type, :Type => Name, :Default => :Catalog, :Required => true
448
+ field :Version, :Type => Name, :Version => "1.4"
449
+ field :Pages, :Type => Dictionary, :Required => true
450
+ field :PageLabels, :Type => Dictionary, :Version => "1.3"
451
+ field :Names, :Type => Dictionary, :Version => "1.2"
452
+ field :Dests, :Type => Dictionary, :Version => "1.1"
453
+ field :ViewerPreferences, :Type => ViewerPreferences, :Version => "1.2"
454
+ field :PageLayout, :Type => Name, :Default => PageLayout::SINGLE
455
+ field :PageMode, :Type => Name, :Default => PageMode::NONE
456
+ field :Outlines, :Type => Dictionary
457
+ field :Threads, :Type => Array, :Version => "1.1"
458
+ field :OpenAction, :Type => [ Array, Dictionary ], :Version => "1.1"
459
+ field :AA, :Type => Dictionary, :Version => "1.4"
460
+ field :URI, :Type => Dictionary, :Version => "1.1"
461
+ field :AcroForm, :Type => Dictionary, :Version => "1.2"
462
+ field :Metadata, :Type => Stream, :Version => "1.4"
463
+ field :StructTreeRoot, :Type => Dictionary, :Version => "1.3"
464
+ field :MarkInfo, :Type => Dictionary, :Version => "1.4"
465
+ field :Lang, :Type => String, :Version => "1.4"
466
+ field :SpiderInfo, :Type => Dictionary, :Version => "1.3"
467
+ field :OutputIntents, :Type => Array, :Version => "1.4"
468
+ field :PieceInfo, :Type => Dictionary, :Version => "1.4"
469
+ field :OCProperties, :Type => Dictionary, :Version => "1.5"
470
+ field :Perms, :Type => Dictionary, :Version => "1.5"
471
+ field :Legal, :Type => Dictionary, :Version => "1.5"
472
+ field :Requirements, :Type => Array, :Version => "1.7"
473
+ field :Collection, :Type => Dictionary, :Version => "1.7"
474
+ field :NeedsRendering, :Type => Boolean, :Version => "1.7", :Default => false
475
+ field :Extensions, :Type => Dictionary, :Version => "1.7", :ExtensionLevel => 3
476
+
477
+ def initialize(hash = {})
478
+ set_indirect(true)
479
+
480
+ super(hash)
481
+ end
482
+
483
+ end
484
+
485
485
  end
@@ -1,5 +1,4 @@
1
1
  =begin
2
-
3
2
  = File
4
3
  dictionary.rb
5
4
 
@@ -39,6 +38,7 @@ module Origami
39
38
  @@regexp_open = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.first) + WHITESPACES)
40
39
  @@regexp_close = Regexp.new(WHITESPACES + Regexp.escape(TOKENS.last) + WHITESPACES)
41
40
 
41
+ @@cast_fingerprints = {}
42
42
  attr_reader :strings_cache, :names_cache, :xref_cache
43
43
 
44
44
  #
@@ -76,7 +76,7 @@ module Origami
76
76
  end
77
77
  end
78
78
 
79
- def self.parse(stream) #:nodoc:
79
+ def self.parse(stream, parser = nil) #:nodoc:
80
80
 
81
81
  offset = stream.pos
82
82
 
@@ -86,28 +86,44 @@ module Origami
86
86
 
87
87
  pairs = {}
88
88
  while stream.skip(@@regexp_close).nil? do
89
- key = Name.parse(stream)
89
+ key = Name.parse(stream, parser)
90
90
 
91
91
  type = Object.typeof(stream)
92
92
  if type.nil?
93
93
  raise InvalidDictionaryObjectError, "Invalid object for field #{key.to_s}"
94
94
  end
95
- value = type.parse(stream)
96
95
 
96
+ value = type.parse(stream, parser)
97
97
  pairs[key] = value
98
98
  end
99
99
 
100
100
  dict =
101
101
  if Origami::OPTIONS[:enable_type_guessing]
102
- type = pairs[Name.new(:Type)]
103
- if type.is_a?(Name) and DICT_SPECIAL_TYPES.include?(type.value)
104
- DICT_SPECIAL_TYPES[type.value].new(pairs)
102
+ guessed_type = self.guess_type(pairs)
103
+
104
+ if Origami::OPTIONS[:enable_type_propagation]
105
+ guessed_type.new(
106
+ Hash[
107
+ pairs.map {|key, value|
108
+ hint_type = guessed_type.hint_type(key.value)
109
+ if hint_type.is_a?(::Array) and not value.is_a?(Reference) # Choose best match
110
+ hint_type.find {|type| type.native_type == value.native_type}
111
+ end
112
+
113
+ if hint_type.is_a?(Class) and hint_type.native_type == value.native_type
114
+ [key, value.cast_to(hint_type)]
115
+ elsif hint_type and value.is_a?(Reference) and parser
116
+ parser.defer_type_cast(value, hint_type)
117
+ [key, value]
118
+ else
119
+ [key, value]
120
+ end
121
+ }])
105
122
  else
106
- Dictionary.new(pairs)
123
+ guessed_type.new(pairs)
107
124
  end
108
-
109
125
  else
110
- Dictionary.new(pairs)
126
+ self.new(pairs)
111
127
  end
112
128
 
113
129
  dict.file_offset = offset
@@ -117,7 +133,7 @@ module Origami
117
133
 
118
134
  alias to_h to_hash
119
135
 
120
- def to_s(indent = 1) #:nodoc:
136
+ def to_s(indent = 1) #:nodoc:
121
137
  if indent > 0
122
138
  content = TOKENS.first + EOL
123
139
  self.each_pair do |key,value|
@@ -177,9 +193,26 @@ module Origami
177
193
  super(key.to_o)
178
194
  end
179
195
 
180
- alias each each_value
196
+ def cast_to(type)
197
+ super(type)
198
+
199
+ cast = type.new(self)
200
+ cast.parent = self.parent
201
+ cast.no, cast.generation = self.no, self.generation
202
+ if self.is_indirect?
203
+ cast.set_indirect(true)
204
+ cast.set_pdf(self.pdf)
205
+ cast.file_offset = self.file_offset # cast can replace self
206
+ end
181
207
 
182
- def real_type ; Dictionary end
208
+ cast.xref_cache.update(self.xref_cache)
209
+ cast.names_cache.concat(self.names_cache)
210
+ cast.strings_cache.concat(self.strings_cache)
211
+
212
+ cast
213
+ end
214
+
215
+ alias each each_value
183
216
 
184
217
  alias value to_h
185
218
 
@@ -194,6 +227,46 @@ module Origami
194
227
  end
195
228
  end
196
229
 
230
+ def copy
231
+ copy = self.class.new
232
+ self.each_pair do |k,v|
233
+ copy[k] = v.copy
234
+ end
235
+
236
+ copy.parent = @parent
237
+ copy.no, copy.generation = @no, @generation
238
+ copy.set_indirect(true) if is_indirect?
239
+ copy.set_pdf(@pdf) if is_indirect?
240
+ copy
241
+ end
242
+
243
+ def self.native_type; Dictionary end
244
+
245
+ def self.add_type_info(typeclass, key, value) #:nodoc:
246
+ if not @@cast_fingerprints.has_key?(typeclass) and typeclass.superclass != Dictionary and
247
+ @@cast_fingerprints.has_key?(typeclass.superclass)
248
+ @@cast_fingerprints[typeclass] = @@cast_fingerprints[typeclass.superclass].dup
249
+ end
250
+
251
+ @@cast_fingerprints[typeclass] ||= {}
252
+ @@cast_fingerprints[typeclass][key.to_o] = value.to_o
253
+ end
254
+
255
+ def self.guess_type(hash) #:nodoc:
256
+ best_type = self
257
+
258
+ @@cast_fingerprints.each_pair do |typeclass, keys|
259
+ best_type = typeclass if keys.all? { |k,v|
260
+ hash.has_key?(k) and hash[k] == v
261
+ } and typeclass < best_type
262
+ end
263
+
264
+ best_type
265
+ end
266
+
267
+ def self.hint_type(name); nil end #:nodoc:
268
+
197
269
  end #class
198
270
 
199
- end # Origami
271
+ end
272
+ # Origami
@@ -23,6 +23,12 @@
23
23
 
24
24
  =end
25
25
 
26
+ begin
27
+ require 'openssl' if Origami::OPTIONS[:use_openssl]
28
+ rescue LoadError
29
+ Origami::OPTIONS[:use_openssl] = false
30
+ end
31
+
26
32
  require 'digest/md5'
27
33
  require 'digest/sha2'
28
34
 
@@ -163,20 +169,18 @@ module Origami
163
169
  obj.equal?(encrypt_dict[:Perms]) or
164
170
  (obj.parent.is_a?(Signature::DigitalSignature) and obj.equal?(obj.parent[:Contents]))
165
171
 
166
- obj.extend(Encryption::EncryptedString)
172
+ obj.extend(Encryption::EncryptedString) unless obj.is_a?(Encryption::EncryptedString)
167
173
  obj.encryption_handler = handler
168
174
  obj.encryption_key = encryption_key
169
175
  obj.algorithm = str_algo
170
- obj.decrypted = false
171
176
  obj.decrypt!
172
177
 
173
178
  when Stream
174
179
  next if obj.is_a?(XRefStream) or (not encrypt_metadata and obj.equal?(metadata))
175
- obj.extend(Encryption::EncryptedStream)
180
+ obj.extend(Encryption::EncryptedStream) unless obj.is_a?(Encryption::EncryptedStream)
176
181
  obj.encryption_handler = handler
177
182
  obj.encryption_key = encryption_key
178
183
  obj.algorithm = stm_algo
179
- obj.decrypted = false
180
184
  end
181
185
  end
182
186
  end
@@ -293,6 +297,26 @@ module Origami
293
297
  #
294
298
  module Encryption
295
299
 
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*")
305
+ end
306
+
307
+ #
308
+ # Generates _n_ random bytes from a crypto PRNG.
309
+ #
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
+
296
320
  module EncryptedDocument
297
321
 
298
322
  attr_writer :encryption_key
@@ -442,7 +466,7 @@ module Origami
442
466
  if @algorithm == ARC4 or @algorithm == Identity
443
467
  @algorithm.encrypt(key, self.value)
444
468
  else
445
- iv = ::Array.new(AES::BLOCKSIZE) { rand(256) }.pack('C*')
469
+ iv = Encryption.rand_bytes(AES::BLOCKSIZE)
446
470
  @algorithm.encrypt(key, iv, self.value)
447
471
  end
448
472
 
@@ -482,7 +506,7 @@ module Origami
482
506
  if @algorithm == ARC4 or @algorithm == Identity
483
507
  @algorithm.encrypt(key, self.rawdata)
484
508
  else
485
- iv = ::Array.new(AES::BLOCKSIZE) { rand(256) }.pack('C*')
509
+ iv = Encryption.rand_bytes(AES::BLOCKSIZE)
486
510
  @algorithm.encrypt(key, iv, @rawdata)
487
511
  end
488
512
 
@@ -1043,6 +1067,7 @@ module Origami
1043
1067
  module Standard
1044
1068
 
1045
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.8'
1046
1071
 
1047
1072
  #
1048
1073
  # Permission constants for encrypted documents.
@@ -1090,6 +1115,7 @@ module Origami
1090
1115
 
1091
1116
  if self.R < 5
1092
1117
  padded = pad_password(userpassword)
1118
+ padded.force_encoding('binary') if RUBY_VERSION > '1.8'
1093
1119
 
1094
1120
  padded << self.O
1095
1121
  padded << [ self.P ].pack("i")
@@ -1097,7 +1123,7 @@ module Origami
1097
1123
  padded << fileid
1098
1124
 
1099
1125
  encrypt_metadata = self.EncryptMetadata != false
1100
- padded << "\xFF\xFF\xFF\xFF" if self.R >= 4 and not encrypt_metadata
1126
+ padded << [ -1 ].pack("i") if self.R >= 4 and not encrypt_metadata
1101
1127
 
1102
1128
  key = Digest::MD5.digest(padded)
1103
1129
 
@@ -1135,9 +1161,9 @@ module Origami
1135
1161
  oks = self.O[40, 8]
1136
1162
 
1137
1163
  if self.R == 5
1138
- okey = Digest::SHA256.digest(passwd + oks)
1164
+ okey = Digest::SHA256.digest(passwd + oks + self.U)
1139
1165
  else
1140
- okey = compute_hardened_hash(passwd, oks)
1166
+ okey = compute_hardened_hash(passwd, oks, self.U)
1141
1167
  end
1142
1168
 
1143
1169
  iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
@@ -1163,35 +1189,31 @@ module Origami
1163
1189
  upass = password_to_utf8(userpassword)
1164
1190
  opass = password_to_utf8(ownerpassword)
1165
1191
 
1166
- uvs, uks, ovs, oks = ::Array.new(4) { ::Array.new(8) { rand(255) }.pack("C*") }
1167
- file_key = ::Array.new(32) { rand(256) }.pack("C*")
1192
+ uvs, uks, ovs, oks = ::Array.new(4) { Encryption.rand_bytes(8) }
1193
+ file_key = Encryption.strong_rand_bytes(32)
1168
1194
  iv = ::Array.new(AES::BLOCKSIZE, 0).pack("C*")
1169
1195
 
1170
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
1171
1199
  ukey = Digest::SHA256.digest(upass + uks)
1172
- okey = Digest::SHA256.digest(opass + oks)
1200
+ okey = Digest::SHA256.digest(opass + oks + self.U)
1173
1201
  else
1202
+ self.U = compute_hardened_hash(upass, uvs) + uvs + uks
1203
+ self.O = compute_hardened_hash(opass, ovs, self.U) + ovs + oks
1174
1204
  ukey = compute_hardened_hash(upass, uks)
1175
- okey = compute_hardened_hash(upass, oks)
1205
+ okey = compute_hardened_hash(opass, oks, self.U)
1176
1206
  end
1177
1207
 
1178
1208
  self.UE = AES.new(ukey, iv, false).encrypt(file_key)[iv.size, 32]
1179
1209
  self.OE = AES.new(okey, iv, false).encrypt(file_key)[iv.size, 32]
1180
-
1181
- if self.R == 5
1182
- self.U = Digest::SHA256.digest(upass + uvs) + uvs + uks
1183
- self.O = Digest::SHA256.digest(opass + ovs + self.U) + ovs + oks
1184
- else
1185
- self.U = compute_hardened_hash(upass, uvs) + uvs + uks
1186
- self.O = compute_hardened_hash(opass, ovs, self.U) + ovs + oks
1187
- end
1188
1210
 
1189
1211
  perms =
1190
1212
  [ self.P ].pack("V") + # 0-3
1191
- "\xff" * 4 + # 4-7
1213
+ [ -1 ].pack("V") + # 4-7
1192
1214
  (self.EncryptMetadata == true ? "T" : "F") + # 8
1193
1215
  "adb" + # 9-11
1194
- "\x00" * 4 # 12-15
1216
+ [ 0 ].pack("V") # 12-15
1195
1217
 
1196
1218
  self.Perms = AES.new(file_key, iv, false).encrypt(perms)[iv.size, 16]
1197
1219
 
@@ -1294,7 +1316,7 @@ module Origami
1294
1316
 
1295
1317
  19.times { |i| user_key = ARC4.encrypt(xor(key,i+1), user_key) }
1296
1318
 
1297
- user_key.ljust(32, "\xFF")
1319
+ user_key.ljust(32, 0xFF.chr)
1298
1320
  end
1299
1321
  end
1300
1322
 
data/lib/origami/file.rb CHANGED
@@ -182,8 +182,7 @@ module Origami
182
182
  # A class representing a file outside the current PDF file.
183
183
  #
184
184
  class ExternalFile < FileSpec
185
-
186
- field :Type, :Type => Name, :Default => :FileSpec, :Required => true
185
+ field :Type, :Type => Name, :Default => :FileSpec #, :Required => true
187
186
 
188
187
  #
189
188
  # Creates a new external file specification.