origami 1.2.1 → 1.2.2

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 (121) hide show
  1. data/README +1 -1
  2. data/bin/gui/hexview.rb +1 -1
  3. data/bin/gui/menu.rb +4 -4
  4. data/bin/gui/textview.rb +6 -4
  5. data/bin/gui/treeview.rb +4 -4
  6. data/bin/gui/walker.rb +1 -1
  7. data/bin/pdf2graph +1 -1
  8. data/bin/pdf2pdfa +1 -1
  9. data/bin/pdf2ruby +1 -1
  10. data/bin/pdfcocoon +1 -1
  11. data/bin/pdfcop +1 -1
  12. data/bin/pdfdecompress +1 -1
  13. data/bin/pdfdecrypt +1 -1
  14. data/bin/pdfencrypt +1 -1
  15. data/bin/pdfextract +75 -14
  16. data/bin/pdfmetadata +1 -1
  17. data/bin/shell/.irbrc +1 -1
  18. data/{origami.rb → lib/origami.rb} +3 -3
  19. data/{origami → lib/origami}/3d.rb +0 -0
  20. data/{origami → lib/origami}/acroform.rb +2 -2
  21. data/{origami → lib/origami}/actions.rb +0 -0
  22. data/{origami → lib/origami}/annotations.rb +0 -0
  23. data/{origami → lib/origami}/array.rb +0 -0
  24. data/{origami → lib/origami}/boolean.rb +0 -0
  25. data/{origami → lib/origami}/catalog.rb +0 -0
  26. data/{origami → lib/origami}/destinations.rb +0 -0
  27. data/{origami → lib/origami}/dictionary.rb +0 -0
  28. data/{origami → lib/origami}/docmdp.rb +0 -0
  29. data/{origami → lib/origami}/encryption.rb +9 -7
  30. data/{origami → lib/origami}/export.rb +0 -0
  31. data/lib/origami/extensions/fdf.rb +257 -0
  32. data/{origami/adobe → lib/origami/extensions}/ppklite.rb +3 -1
  33. data/{origami → lib/origami}/file.rb +0 -0
  34. data/{origami → lib/origami}/filters.rb +0 -0
  35. data/{origami → lib/origami}/filters/ascii.rb +0 -0
  36. data/{origami → lib/origami}/filters/ccitt.rb +0 -1
  37. data/{origami → lib/origami}/filters/crypt.rb +0 -0
  38. data/{origami → lib/origami}/filters/dct.rb +0 -0
  39. data/{origami → lib/origami}/filters/flate.rb +0 -0
  40. data/{origami → lib/origami}/filters/jbig2.rb +0 -0
  41. data/{origami → lib/origami}/filters/jpx.rb +0 -0
  42. data/{origami → lib/origami}/filters/lzw.rb +0 -0
  43. data/{origami → lib/origami}/filters/predictors.rb +0 -0
  44. data/{origami → lib/origami}/filters/runlength.rb +0 -0
  45. data/{origami → lib/origami}/font.rb +0 -0
  46. data/{origami → lib/origami}/functions.rb +0 -0
  47. data/{origami → lib/origami}/graphics.rb +0 -0
  48. data/{origami → lib/origami}/graphics/colors.rb +45 -23
  49. data/{origami → lib/origami}/graphics/instruction.rb +0 -0
  50. data/{origami → lib/origami}/graphics/path.rb +0 -0
  51. data/{origami → lib/origami}/graphics/patterns.rb +0 -0
  52. data/{origami → lib/origami}/graphics/render.rb +0 -0
  53. data/{origami → lib/origami}/graphics/state.rb +2 -2
  54. data/{origami → lib/origami}/graphics/text.rb +0 -0
  55. data/{origami → lib/origami}/graphics/xobject.rb +219 -0
  56. data/{origami → lib/origami}/header.rb +0 -0
  57. data/{origami → lib/origami}/javascript.rb +0 -0
  58. data/{origami → lib/origami}/linearization.rb +0 -0
  59. data/{origami → lib/origami}/metadata.rb +0 -0
  60. data/{origami → lib/origami}/name.rb +0 -0
  61. data/{origami → lib/origami}/null.rb +0 -0
  62. data/{origami → lib/origami}/numeric.rb +0 -0
  63. data/{origami → lib/origami}/obfuscation.rb +0 -0
  64. data/{origami → lib/origami}/object.rb +7 -2
  65. data/{origami → lib/origami}/outline.rb +0 -0
  66. data/{origami → lib/origami}/outputintents.rb +0 -0
  67. data/{origami → lib/origami}/page.rb +0 -0
  68. data/{origami → lib/origami}/parser.rb +76 -51
  69. data/{origami → lib/origami}/parsers/fdf.rb +9 -6
  70. data/{origami/parsers/pdf/linear.rb → lib/origami/parsers/pdf.rb} +31 -39
  71. data/lib/origami/parsers/pdf/linear.rb +84 -0
  72. data/lib/origami/parsers/ppklite.rb +93 -0
  73. data/{origami → lib/origami}/pdf.rb +6 -3
  74. data/{origami → lib/origami}/reference.rb +0 -0
  75. data/{origami → lib/origami}/signature.rb +170 -19
  76. data/{origami → lib/origami}/stream.rb +9 -0
  77. data/{origami → lib/origami}/string.rb +0 -0
  78. data/{origami → lib/origami}/trailer.rb +0 -0
  79. data/{origami → lib/origami}/webcapture.rb +0 -0
  80. data/{origami → lib/origami}/xfa.rb +0 -0
  81. data/{origami → lib/origami}/xreftable.rb +3 -7
  82. data/samples/README.txt +45 -0
  83. data/samples/actions/launch/calc.rb +87 -0
  84. data/samples/actions/launch/winparams.rb +22 -0
  85. data/samples/actions/loop/loopgoto.rb +24 -0
  86. data/samples/actions/loop/loopnamed.rb +21 -0
  87. data/samples/actions/named/named.rb +31 -0
  88. data/samples/actions/samba/smbrelay.rb +26 -0
  89. data/samples/actions/triggerevents/trigger.rb +75 -0
  90. data/samples/actions/webbug/submitform.js +26 -0
  91. data/samples/actions/webbug/webbug-browser.rb +68 -0
  92. data/samples/actions/webbug/webbug-js.rb +67 -0
  93. data/samples/actions/webbug/webbug-reader.rb +90 -0
  94. data/samples/attachments/attach.rb +40 -0
  95. data/samples/attachments/attached.txt +1 -0
  96. data/samples/crypto/crypto.rb +28 -0
  97. data/samples/digsig/signed.rb +46 -0
  98. data/samples/exploits/cve-2008-2992-utilprintf.rb +87 -0
  99. data/samples/exploits/cve-2009-0927-geticon.rb +65 -0
  100. data/samples/exploits/exploit_customdictopen.rb +55 -0
  101. data/samples/exploits/getannots.rb +69 -0
  102. data/samples/flash/flash.rb +31 -0
  103. data/samples/flash/helloworld.swf +0 -0
  104. data/samples/javascript/attached.txt +1 -0
  105. data/samples/javascript/js.rb +52 -0
  106. data/{tests → test}/ts_pdf.rb +1 -1
  107. metadata +109 -95
  108. data/origami/adobe/fdf.rb +0 -259
  109. data/origami/parsers/pdf.rb +0 -27
  110. data/origami/parsers/ppklite.rb +0 -86
  111. data/tests/dataset/test.dummycrt +0 -28
  112. data/tests/dataset/test.dummykey +0 -27
  113. data/tests/tc_actions.rb +0 -32
  114. data/tests/tc_annotations.rb +0 -85
  115. data/tests/tc_pages.rb +0 -37
  116. data/tests/tc_pdfattach.rb +0 -24
  117. data/tests/tc_pdfencrypt.rb +0 -110
  118. data/tests/tc_pdfnew.rb +0 -32
  119. data/tests/tc_pdfparse.rb +0 -98
  120. data/tests/tc_pdfsig.rb +0 -37
  121. data/tests/tc_streams.rb +0 -129
@@ -20,11 +20,10 @@
20
20
  =end
21
21
 
22
22
  require 'origami/parser'
23
- require 'origami/adobe/fdf'
24
23
 
25
24
  module Origami
26
25
 
27
- class Adobe::FDF
26
+ class FDF
28
27
  class Parser < Origami::Parser
29
28
  def parse(stream) #:nodoc:
30
29
  super
@@ -33,11 +32,15 @@ module Origami
33
32
  fdf.header = Adobe::FDF::Header.parse(stream)
34
33
  @options[:callback].call(fdf.header)
35
34
 
36
- parse_objects(fdf)
37
- parse_xreftable(fdf)
38
- parse_trailer(fdf)
35
+ loop do
36
+ break if (object = parse_object).nil?
37
+ fdf << object
38
+ end
39
+
40
+ fdf.revisions.first.xreftable = parse_xreftable
41
+ fdf.revisions.first.trailer = parse_trailer
39
42
 
40
- addrbk
43
+ fdf
41
44
  end
42
45
  end
43
46
  end
@@ -1,10 +1,14 @@
1
1
  =begin
2
2
 
3
3
  = File
4
- parsers/linear.rb
4
+ parsers/pdf.rb
5
5
 
6
6
  = Info
7
- Origami is free software: you can redistribute it and/or modify
7
+ This file is part of Origami, PDF manipulation framework for Ruby
8
+ Copyright (C) 2010 Guillaume Delugré <guillaume@security-labs.org>
9
+ All right reserved.
10
+
11
+ Origami is free software: you can redistribute it and/or modify
8
12
  it under the terms of the GNU Lesser General Public License as published by
9
13
  the Free Software Foundation, either version 3 of the License, or
10
14
  (at your option) any later version.
@@ -19,21 +23,29 @@
19
23
 
20
24
  =end
21
25
 
22
-
23
26
  require 'origami/parser'
24
- require 'origami/pdf'
25
27
 
26
28
  module Origami
27
-
29
+
28
30
  class PDF
31
+ class Parser < Origami::Parser
32
+ def initialize(params = {})
33
+ options =
34
+ {
35
+ :password => '', # Default password being tried when opening a protected document.
36
+ :prompt_password => Proc.new {
37
+ print "Password: "
38
+ gets.chomp
39
+ }, # Callback procedure to prompt password when document is encrypted.
40
+ :force => false # Force PDF header detection
41
+ }.update(params)
42
+
43
+ super(options)
44
+ end
29
45
 
30
- #
31
- # Create a new PDF linear Parser.
32
- #
33
- class LinearParser < Origami::Parser
34
- def parse(stream)
35
- super
36
-
46
+ private
47
+
48
+ def parse_initialize #:nodoc:
37
49
  if @options[:force] == true
38
50
  @data.skip_until(/%PDF-/).nil?
39
51
  @data.pos = @data.pos - 5
@@ -52,34 +64,11 @@ module Origami
52
64
  raise e
53
65
  end
54
66
  end
55
-
56
- #
57
- # Parse each revision
58
- #
59
- revision = 0
60
- until @data.eos? do
61
-
62
- begin
63
-
64
- pdf.add_new_revision unless revision.zero?
65
- revision = revision.succ
66
-
67
- info "...Parsing revision #{pdf.revisions.size}..."
68
- parse_objects(pdf)
69
- parse_xreftable(pdf)
70
- parse_trailer(pdf)
71
-
72
- rescue SystemExit
73
- raise
74
- rescue Exception => e
75
- error "Cannot read : " + (@data.peek(10) + "...").inspect
76
- error "Stopped on exception : " + e.message
77
-
78
- break
79
- end
80
-
81
- end
82
67
 
68
+ pdf
69
+ end
70
+
71
+ def parse_finalize(pdf) #:nodoc:
83
72
  warn "This file has been linearized." if pdf.is_linearized?
84
73
 
85
74
  #
@@ -109,5 +98,8 @@ module Origami
109
98
  end
110
99
  end
111
100
  end
101
+
112
102
  end
113
103
 
104
+ require 'origami/parsers/pdf/linear'
105
+
@@ -0,0 +1,84 @@
1
+ =begin
2
+
3
+ = File
4
+ parsers/linear.rb
5
+
6
+ = Info
7
+ Origami is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Origami is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+
22
+
23
+ require 'origami/parser'
24
+ require 'origami/pdf'
25
+
26
+ module Origami
27
+
28
+ class PDF
29
+
30
+ #
31
+ # Create a new PDF linear Parser.
32
+ #
33
+ class LinearParser < Parser
34
+ def parse(stream)
35
+ super
36
+
37
+ pdf = parse_initialize
38
+
39
+ #
40
+ # Parse each revision
41
+ #
42
+ revision = 0
43
+ until @data.eos? do
44
+
45
+ begin
46
+ pdf.add_new_revision unless revision.zero?
47
+ revision = revision + 1
48
+
49
+ info "...Parsing revision #{pdf.revisions.size}..."
50
+ loop do
51
+ break if (object = parse_object).nil?
52
+ pdf.insert(object)
53
+ end
54
+
55
+ pdf.revisions.last.xreftable = parse_xreftable
56
+
57
+ trailer = parse_trailer
58
+ pdf.revisions.last.trailer = trailer
59
+
60
+ xrefstm = pdf.get_object_by_offset(trailer.startxref) ||
61
+ (pdf.get_object_by_offset(trailer.XRefStm) if trailer.has_field? :XRefStm)
62
+
63
+ if not xrefstm.nil?
64
+ debug "Found a XRefStream for this revision at #{xrefstm.reference}"
65
+ pdf.revisions.last.xrefstm = xrefstm
66
+ end
67
+
68
+ rescue SystemExit
69
+ raise
70
+ rescue Exception => e
71
+ error "Cannot read : " + (@data.peek(10) + "...").inspect
72
+ error "Stopped on exception : " + e.message
73
+
74
+ break
75
+ end
76
+
77
+ end
78
+
79
+ parse_finalize(pdf)
80
+ end
81
+ end
82
+ end
83
+ end
84
+
@@ -0,0 +1,93 @@
1
+ =begin
2
+
3
+ = File
4
+ parsers/ppklite.rb
5
+
6
+ = Info
7
+ Origami is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU Lesser General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Origami is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU Lesser General Public License for more details.
16
+
17
+ You should have received a copy of the GNU Lesser General Public License
18
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
19
+
20
+ =end
21
+
22
+ require 'origami/parser'
23
+
24
+ module Origami
25
+
26
+ module Adobe
27
+
28
+ class PPKLite
29
+
30
+ class Parser < Origami::Parser
31
+ def parse(stream) #:nodoc:
32
+ super
33
+
34
+ addrbk = Adobe::PPKLite.new
35
+ addrbk.header = Adobe::PPKLite::Header.parse(stream)
36
+ @options[:callback].call(addrbk.header)
37
+
38
+ loop do
39
+ break if (object = parse_object).nil?
40
+ addrbk << object
41
+ end
42
+
43
+ addrbk.revisions.first.xreftable = parse_xreftable
44
+ addrbm.revisions.first.trailer = parse_trailer
45
+ book_specialize_entries(addrbk)
46
+
47
+ addrbk
48
+ end
49
+
50
+ def book_specialize_entries(addrbk) #:nodoc:
51
+ addrbk.revisions.first.body.each_pair do |ref, obj|
52
+
53
+ if obj.is_a?(Dictionary)
54
+
55
+ if obj[:Type] == :Catalog
56
+
57
+ o = Adobe::PPKLite::Catalog.new(obj)
58
+ o.generation, o.no, o.file_offset = obj.generation, obj.no, obj.file_offset
59
+
60
+ if o.PPK.is_a?(Dictionary) and o.PPK[:Type] == :PPK
61
+ o.PPK = Adobe::PPKLite::PPK.new(o.PPK)
62
+
63
+ if o.PPK.User.is_a?(Dictionary) and o.PPK.User[:Type] == :User
64
+ o.PPK.User = Adobe::PPKLite::UserList.new(o.PPK.User)
65
+ end
66
+
67
+ if o.PPK.AddressBook.is_a?(Dictionary) and o.PPK.AddressBook[:Type] == :AddressBook
68
+ o.PPK.AddressBook = Adobe::PPKLite::AddressList.new(o.PPK.AddressBook)
69
+ end
70
+ end
71
+
72
+ addrbk.revisions.first.body[ref] = o
73
+
74
+ elsif obj[:ABEType] == Adobe::PPKLite::Descriptor::USER
75
+ o = Adobe::PPKLite::User.new(obj)
76
+ o.generation, o.no, o.file_offset = obj.generation, obj.no, obj.file_offset
77
+
78
+ addrbk.revisions.first.body[ref] = o
79
+ elsif obj[:ABEType] == Adobe::PPKLite::Descriptor::CERTIFICATE
80
+ o = Adobe::PPKLite::Certificate.new(obj)
81
+ o.generation, o.no, o.file_offset = obj.generation, obj.no, obj.file_offset
82
+
83
+ addrbk.revisions.first.body[ref] = o
84
+ end
85
+
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
@@ -57,10 +57,12 @@ require 'origami/xfa'
57
57
  require 'origami/javascript'
58
58
  require 'origami/outputintents'
59
59
 
60
+ require 'origami/parsers/pdf'
61
+
60
62
  module Origami
61
63
 
62
- VERSION = "1.2.1"
63
- REVISION = "$Revision: rev 130/, 2011/10/05 12:33:53 darko $" #:nodoc:
64
+ VERSION = "1.2.2"
65
+ REVISION = "$Revision: rev 135/, 2011/10/17 11:59:41 $" #:nodoc:
64
66
 
65
67
  #
66
68
  # Global options for Origami.
@@ -181,7 +183,7 @@ module Origami
181
183
  #
182
184
  # Reads and parses a PDF file from disk.
183
185
  #
184
- def read(filename, options = {:verbosity => Parser::VERBOSE_INSANE})
186
+ def read(filename, options = {})
185
187
  filename = File.expand_path(filename) if filename.is_a?(::String)
186
188
  PDF::LinearParser.new(options).parse(filename)
187
189
  end
@@ -293,6 +295,7 @@ module Origami
293
295
  if path.respond_to?(:write)
294
296
  fd = path
295
297
  else
298
+ path = File.expand_path(path)
296
299
  fd = File.open(path, 'w').binmode
297
300
  end
298
301
 
File without changes
@@ -19,9 +19,71 @@
19
19
 
20
20
  =end
21
21
 
22
+ require 'openssl'
23
+ require 'digest/sha1'
24
+
22
25
  module Origami
23
26
 
24
27
  class PDF
28
+
29
+ class SignatureError < Exception #:nodoc:
30
+ end
31
+
32
+ #
33
+ # Verify a document signature.
34
+ # Options:
35
+ # _:trusted_: an array of trusted X509 certificates.
36
+ # If no argument is passed, embedded certificates are treated as trusted.
37
+ #
38
+ def verify(options = {})
39
+ params =
40
+ {
41
+ :trusted => []
42
+ }.update(options)
43
+
44
+ digsig = self.signature
45
+
46
+ unless digsig[:Contents].is_a?(String)
47
+ raise SignatureError, "Invalid digital signature contents"
48
+ end
49
+
50
+ store = OpenSSL::X509::Store.new
51
+ params[:trusted].each do |ca| store.add_cert(ca) end
52
+ flags = 0
53
+ flags |= OpenSSL::PKCS7::NOVERIFY if params[:trusted].empty?
54
+
55
+ stream = StringScanner.new(self.original_data)
56
+ stream.pos = digsig[:Contents].file_offset
57
+ Object.typeof(stream).parse(stream)
58
+ endofsig_offset = stream.pos
59
+ stream.terminate
60
+
61
+ s1,l1,s2,l2 = digsig.ByteRange
62
+ if s1.value != 0 or
63
+ (s2.value + l2.value) != self.original_data.size or
64
+ (s1.value + l1.value) != digsig[:Contents].file_offset or
65
+ s2.value != endofsig_offset
66
+
67
+ raise SignatureError, "Invalid signature byte range"
68
+ end
69
+
70
+ data = self.original_data[s1,l1] + self.original_data[s2,l2]
71
+
72
+ case digsig.SubFilter.value.to_s
73
+ when 'adbe.pkcs7.detached'
74
+ flags |= OpenSSL::PKCS7::DETACHED
75
+ p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
76
+ raise SignatureError, "Not a PKCS7 detached signature" unless p7.detached?
77
+ p7.verify([], store, data, flags)
78
+
79
+ when 'adbe.pkcs7.sha1'
80
+ p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
81
+ p7.verify([], store, nil, flags) and p7.data == Digest::SHA1.digest(data)
82
+
83
+ else
84
+ raise NotImplementedError, "Unsupported method #{digsig.SubFilter}"
85
+ end
86
+ end
25
87
 
26
88
  #
27
89
  # Sign the document with the given key and x509 certificate.
@@ -29,11 +91,21 @@ module Origami
29
91
  # _key_:: The private key associated with the certificate.
30
92
  # _ca_:: Optional CA certificates used to sign the user certificate.
31
93
  #
32
- def sign(certificate, key, ca = [], annotation = nil, location = nil, contact = nil, reason = nil)
94
+ def sign(certificate, key, options = {})
33
95
 
34
96
  unless Origami::OPTIONS[:use_openssl]
35
97
  fail "OpenSSL is not present or has been disabled."
36
98
  end
99
+
100
+ params =
101
+ {
102
+ :method => "adbe.pkcs7.detached",
103
+ :ca => [],
104
+ :annotation => nil,
105
+ :location => nil,
106
+ :contact => nil,
107
+ :reason => nil
108
+ }.update(options)
37
109
 
38
110
  unless certificate.is_a?(OpenSSL::X509::Certificate)
39
111
  raise TypeError, "A OpenSSL::X509::Certificate object must be passed."
@@ -43,19 +115,53 @@ module Origami
43
115
  raise TypeError, "A OpenSSL::PKey::RSA object must be passed."
44
116
  end
45
117
 
118
+ ca = params[:ca]
46
119
  unless ca.is_a?(::Array)
47
120
  raise TypeError, "Expected an Array of CA certificate."
48
121
  end
49
122
 
123
+ annotation = params[:annotation]
50
124
  unless annotation.nil? or annotation.is_a?(Annotation::Widget::Signature)
51
125
  raise TypeError, "Expected a Annotation::Widget::Signature object."
52
126
  end
53
-
54
- def signfield_size(certificate, key, ca = []) #;nodoc:
55
- datatest = "abcdefghijklmnopqrstuvwxyz"
56
- OpenSSL::PKCS7.sign(certificate, key, datatest, ca, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der.size + 128
127
+
128
+ case params[:method]
129
+ when 'adbe.pkcs7.detached'
130
+ signfield_size = lambda{|crt,key,ca|
131
+ datatest = "abcdefghijklmnopqrstuvwxyz"
132
+ OpenSSL::PKCS7.sign(
133
+ crt,
134
+ key,
135
+ datatest,
136
+ ca,
137
+ OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
138
+ ).to_der.size + 128
139
+ }
140
+ when 'adbe.pkcs7.sha1'
141
+ signfield_size = lambda{|crt,key,ca|
142
+ datatest = "abcdefghijklmnopqrstuvwxyz"
143
+ OpenSSL::PKCS7.sign(
144
+ crt,
145
+ key,
146
+ Digest::SHA1.digest(datatest),
147
+ ca,
148
+ OpenSSL::PKCS7::BINARY
149
+ ).to_der.size + 128
150
+ }
151
+
152
+ when 'adbe.x509.rsa_sha1'
153
+ signfield_size = lambda{|crt,key,ca|
154
+ datatest = "abcdefghijklmnopqrstuvwxyz"
155
+ key.private_encrypt(
156
+ Digest::SHA1.digest(datatest)
157
+ ).size + 128
158
+ }
159
+ raise NotImplementedError, "Unsupported method #{params[:method].inspect}"
160
+
161
+ else
162
+ raise NotImplementedError, "Unsupported method #{params[:method].inspect}"
57
163
  end
58
-
164
+
59
165
  digsig = Signature::DigitalSignature.new.set_indirect(true)
60
166
 
61
167
  if annotation.nil?
@@ -63,20 +169,30 @@ module Origami
63
169
  annotation.Rect = Rectangle[:llx => 0.0, :lly => 0.0, :urx => 0.0, :ury => 0.0]
64
170
  end
65
171
 
66
- annotation.V = digsig ;
172
+ annotation.V = digsig
67
173
  add_fields(annotation)
68
- self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::SIGNATURESEXIST | InteractiveForm::SigFlags::APPENDONLY
174
+ self.Catalog.AcroForm.SigFlags =
175
+ InteractiveForm::SigFlags::SIGNATURESEXIST | InteractiveForm::SigFlags::APPENDONLY
69
176
 
70
177
  digsig.Type = :Sig #:nodoc:
71
- digsig.Contents = HexaString.new("\x00" * signfield_size(certificate, key, ca)) #:nodoc:
178
+ digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, ca]) #:nodoc:
72
179
  digsig.Filter = Name.new("Adobe.PPKMS") #:nodoc:
73
- digsig.SubFilter = Name.new("adbe.pkcs7.detached") #:nodoc:
180
+ digsig.SubFilter = Name.new(params[:method]) #:nodoc:
74
181
  digsig.ByteRange = [0, 0, 0, 0] #:nodoc:
75
182
 
76
- digsig.Location = HexaString.new(location) if location
77
- digsig.ContactInfo = HexaString.new(contact) if contact
78
- digsig.Reason = HexaString.new(reason) if reason
183
+ digsig.Location = HexaString.new(params[:location]) if params[:location]
184
+ digsig.ContactInfo = HexaString.new(params[:contact]) if params[:contact]
185
+ digsig.Reason = HexaString.new(params[:reason]) if params[:reason]
79
186
 
187
+ if params[:method] == 'adbe.x509.rsa_sha1'
188
+ digsig.Cert =
189
+ if ca.empty?
190
+ HexaString.new(certificate.to_der)
191
+ else
192
+ [ HexaString.new(certificate.to_der) ] + ca.map{ |crt| HexaString.new(crt.to_der) }
193
+ end
194
+ end
195
+
80
196
  #
81
197
  # Flattening the PDF to get file view.
82
198
  #
@@ -105,7 +221,30 @@ module Origami
105
221
  filedata = self.to_bin
106
222
  signable_data = filedata[digsig.ByteRange[0],digsig.ByteRange[1]] + filedata[digsig.ByteRange[2],digsig.ByteRange[3]]
107
223
 
108
- signature = OpenSSL::PKCS7.sign(certificate, key, signable_data, ca, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
224
+ signature =
225
+ case params[:method]
226
+ when 'adbe.pkcs7.detached'
227
+ OpenSSL::PKCS7.sign(
228
+ certificate,
229
+ key,
230
+ signable_data,
231
+ ca,
232
+ OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
233
+ ).to_der
234
+
235
+ when 'adbe.pkcs7.sha1'
236
+ OpenSSL::PKCS7.sign(
237
+ certificate,
238
+ key,
239
+ Digest::SHA1.digest(signable_data),
240
+ ca,
241
+ OpenSSL::PKCS7::BINARY
242
+ ).to_der
243
+
244
+ when 'adbe.x509.rsa_sha1'
245
+ key.private_encrypt(Digest::SHA1.digest(signable_data))
246
+ end
247
+
109
248
  digsig.Contents[0, signature.size] = signature
110
249
 
111
250
  #
@@ -120,7 +259,7 @@ module Origami
120
259
  def is_signed?
121
260
  not self.Catalog.AcroForm.nil? and
122
261
  self.Catalog.AcroForm.has_key?(:SigFlags) and
123
- (self.Catalog.AcroForm[:SigFlags].solve & InteractiveForm::SigFlags::SIGNATURESEXIST != 0)
262
+ (self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::SIGNATURESEXIST != 0)
124
263
  end
125
264
 
126
265
  #
@@ -129,10 +268,10 @@ module Origami
129
268
  #
130
269
  def enable_usage_rights(cert, pkey, *rights)
131
270
 
132
- def signfield_size(certificate, key, ca = []) #:nodoc:
271
+ signfield_size = lambda{|crt, key, ca|
133
272
  datatest = "abcdefghijklmnopqrstuvwxyz"
134
- OpenSSL::PKCS7.sign(certificate, key, datatest, ca, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der.size + 128
135
- end
273
+ OpenSSL::PKCS7.sign(crt, key, datatest, ca, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der.size + 128
274
+ }
136
275
 
137
276
  unless Origami::OPTIONS[:use_openssl]
138
277
  fail "OpenSSL is not present or has been disabled."
@@ -153,7 +292,7 @@ module Origami
153
292
  #self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::APPENDONLY
154
293
 
155
294
  digsig.Type = :Sig #:nodoc:
156
- digsig.Contents = HexaString.new("\x00" * signfield_size(certificate, key, [])) #:nodoc:
295
+ digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, []]) #:nodoc:
157
296
  digsig.Filter = Name.new("Adobe.PPKLite") #:nodoc:
158
297
  digsig.Name = "ARE Acrobat Product v8.0 P23 0002337" #:nodoc:
159
298
  digsig.SubFilter = Name.new("adbe.pkcs7.detached") #:nodoc:
@@ -221,6 +360,18 @@ module Origami
221
360
  not self.Catalog.Perms.nil? and (not self.Catalog.Perms.has_key?(:UR3) or not self.Catalog.Perms.has_key?(:UR))
222
361
  end
223
362
 
363
+ def signature
364
+ raise SignatureError, "Not a signed document" unless self.is_signed?
365
+
366
+ self.each_field do |field|
367
+ if field.FT == :Sig and field.V.is_a?(Dictionary)
368
+ return field.V
369
+ end
370
+ end
371
+
372
+ raise SignatureError, "Cannot find digital signature"
373
+ end
374
+
224
375
  end
225
376
 
226
377
  class Perms < Dictionary