origami 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
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