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.
- data/README +1 -1
- data/bin/gui/hexview.rb +1 -1
- data/bin/gui/menu.rb +4 -4
- data/bin/gui/textview.rb +6 -4
- data/bin/gui/treeview.rb +4 -4
- data/bin/gui/walker.rb +1 -1
- data/bin/pdf2graph +1 -1
- data/bin/pdf2pdfa +1 -1
- data/bin/pdf2ruby +1 -1
- data/bin/pdfcocoon +1 -1
- data/bin/pdfcop +1 -1
- data/bin/pdfdecompress +1 -1
- data/bin/pdfdecrypt +1 -1
- data/bin/pdfencrypt +1 -1
- data/bin/pdfextract +75 -14
- data/bin/pdfmetadata +1 -1
- data/bin/shell/.irbrc +1 -1
- data/{origami.rb → lib/origami.rb} +3 -3
- data/{origami → lib/origami}/3d.rb +0 -0
- data/{origami → lib/origami}/acroform.rb +2 -2
- data/{origami → lib/origami}/actions.rb +0 -0
- data/{origami → lib/origami}/annotations.rb +0 -0
- data/{origami → lib/origami}/array.rb +0 -0
- data/{origami → lib/origami}/boolean.rb +0 -0
- data/{origami → lib/origami}/catalog.rb +0 -0
- data/{origami → lib/origami}/destinations.rb +0 -0
- data/{origami → lib/origami}/dictionary.rb +0 -0
- data/{origami → lib/origami}/docmdp.rb +0 -0
- data/{origami → lib/origami}/encryption.rb +9 -7
- data/{origami → lib/origami}/export.rb +0 -0
- data/lib/origami/extensions/fdf.rb +257 -0
- data/{origami/adobe → lib/origami/extensions}/ppklite.rb +3 -1
- data/{origami → lib/origami}/file.rb +0 -0
- data/{origami → lib/origami}/filters.rb +0 -0
- data/{origami → lib/origami}/filters/ascii.rb +0 -0
- data/{origami → lib/origami}/filters/ccitt.rb +0 -1
- data/{origami → lib/origami}/filters/crypt.rb +0 -0
- data/{origami → lib/origami}/filters/dct.rb +0 -0
- data/{origami → lib/origami}/filters/flate.rb +0 -0
- data/{origami → lib/origami}/filters/jbig2.rb +0 -0
- data/{origami → lib/origami}/filters/jpx.rb +0 -0
- data/{origami → lib/origami}/filters/lzw.rb +0 -0
- data/{origami → lib/origami}/filters/predictors.rb +0 -0
- data/{origami → lib/origami}/filters/runlength.rb +0 -0
- data/{origami → lib/origami}/font.rb +0 -0
- data/{origami → lib/origami}/functions.rb +0 -0
- data/{origami → lib/origami}/graphics.rb +0 -0
- data/{origami → lib/origami}/graphics/colors.rb +45 -23
- data/{origami → lib/origami}/graphics/instruction.rb +0 -0
- data/{origami → lib/origami}/graphics/path.rb +0 -0
- data/{origami → lib/origami}/graphics/patterns.rb +0 -0
- data/{origami → lib/origami}/graphics/render.rb +0 -0
- data/{origami → lib/origami}/graphics/state.rb +2 -2
- data/{origami → lib/origami}/graphics/text.rb +0 -0
- data/{origami → lib/origami}/graphics/xobject.rb +219 -0
- data/{origami → lib/origami}/header.rb +0 -0
- data/{origami → lib/origami}/javascript.rb +0 -0
- data/{origami → lib/origami}/linearization.rb +0 -0
- data/{origami → lib/origami}/metadata.rb +0 -0
- data/{origami → lib/origami}/name.rb +0 -0
- data/{origami → lib/origami}/null.rb +0 -0
- data/{origami → lib/origami}/numeric.rb +0 -0
- data/{origami → lib/origami}/obfuscation.rb +0 -0
- data/{origami → lib/origami}/object.rb +7 -2
- data/{origami → lib/origami}/outline.rb +0 -0
- data/{origami → lib/origami}/outputintents.rb +0 -0
- data/{origami → lib/origami}/page.rb +0 -0
- data/{origami → lib/origami}/parser.rb +76 -51
- data/{origami → lib/origami}/parsers/fdf.rb +9 -6
- data/{origami/parsers/pdf/linear.rb → lib/origami/parsers/pdf.rb} +31 -39
- data/lib/origami/parsers/pdf/linear.rb +84 -0
- data/lib/origami/parsers/ppklite.rb +93 -0
- data/{origami → lib/origami}/pdf.rb +6 -3
- data/{origami → lib/origami}/reference.rb +0 -0
- data/{origami → lib/origami}/signature.rb +170 -19
- data/{origami → lib/origami}/stream.rb +9 -0
- data/{origami → lib/origami}/string.rb +0 -0
- data/{origami → lib/origami}/trailer.rb +0 -0
- data/{origami → lib/origami}/webcapture.rb +0 -0
- data/{origami → lib/origami}/xfa.rb +0 -0
- data/{origami → lib/origami}/xreftable.rb +3 -7
- data/samples/README.txt +45 -0
- data/samples/actions/launch/calc.rb +87 -0
- data/samples/actions/launch/winparams.rb +22 -0
- data/samples/actions/loop/loopgoto.rb +24 -0
- data/samples/actions/loop/loopnamed.rb +21 -0
- data/samples/actions/named/named.rb +31 -0
- data/samples/actions/samba/smbrelay.rb +26 -0
- data/samples/actions/triggerevents/trigger.rb +75 -0
- data/samples/actions/webbug/submitform.js +26 -0
- data/samples/actions/webbug/webbug-browser.rb +68 -0
- data/samples/actions/webbug/webbug-js.rb +67 -0
- data/samples/actions/webbug/webbug-reader.rb +90 -0
- data/samples/attachments/attach.rb +40 -0
- data/samples/attachments/attached.txt +1 -0
- data/samples/crypto/crypto.rb +28 -0
- data/samples/digsig/signed.rb +46 -0
- data/samples/exploits/cve-2008-2992-utilprintf.rb +87 -0
- data/samples/exploits/cve-2009-0927-geticon.rb +65 -0
- data/samples/exploits/exploit_customdictopen.rb +55 -0
- data/samples/exploits/getannots.rb +69 -0
- data/samples/flash/flash.rb +31 -0
- data/samples/flash/helloworld.swf +0 -0
- data/samples/javascript/attached.txt +1 -0
- data/samples/javascript/js.rb +52 -0
- data/{tests → test}/ts_pdf.rb +1 -1
- metadata +109 -95
- data/origami/adobe/fdf.rb +0 -259
- data/origami/parsers/pdf.rb +0 -27
- data/origami/parsers/ppklite.rb +0 -86
- data/tests/dataset/test.dummycrt +0 -28
- data/tests/dataset/test.dummykey +0 -27
- data/tests/tc_actions.rb +0 -32
- data/tests/tc_annotations.rb +0 -85
- data/tests/tc_pages.rb +0 -37
- data/tests/tc_pdfattach.rb +0 -24
- data/tests/tc_pdfencrypt.rb +0 -110
- data/tests/tc_pdfnew.rb +0 -32
- data/tests/tc_pdfparse.rb +0 -98
- data/tests/tc_pdfsig.rb +0 -37
- 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
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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/
|
|
4
|
+
parsers/pdf.rb
|
|
5
5
|
|
|
6
6
|
= Info
|
|
7
|
-
|
|
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
|
-
|
|
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.
|
|
63
|
-
REVISION = "$Revision: rev
|
|
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 = {
|
|
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,
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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 =
|
|
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
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
-
|
|
271
|
+
signfield_size = lambda{|crt, key, ca|
|
|
133
272
|
datatest = "abcdefghijklmnopqrstuvwxyz"
|
|
134
|
-
OpenSSL::PKCS7.sign(
|
|
135
|
-
|
|
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
|
|
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
|