origami 1.2.7 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (162) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -0
  3. data/README.md +112 -0
  4. data/bin/config/pdfcop.conf.yml +232 -233
  5. data/bin/gui/about.rb +27 -37
  6. data/bin/gui/config.rb +108 -117
  7. data/bin/gui/file.rb +416 -365
  8. data/bin/gui/gtkhex.rb +1138 -1153
  9. data/bin/gui/hexview.rb +55 -57
  10. data/bin/gui/imgview.rb +48 -51
  11. data/bin/gui/menu.rb +388 -386
  12. data/bin/gui/properties.rb +114 -130
  13. data/bin/gui/signing.rb +571 -617
  14. data/bin/gui/textview.rb +77 -95
  15. data/bin/gui/treeview.rb +382 -387
  16. data/bin/gui/walker.rb +227 -232
  17. data/bin/gui/xrefs.rb +56 -60
  18. data/bin/pdf2pdfa +53 -57
  19. data/bin/pdf2ruby +212 -228
  20. data/bin/pdfcop +338 -348
  21. data/bin/pdfdecompress +58 -65
  22. data/bin/pdfdecrypt +56 -60
  23. data/bin/pdfencrypt +75 -80
  24. data/bin/pdfexplode +185 -182
  25. data/bin/pdfextract +201 -218
  26. data/bin/pdfmetadata +83 -82
  27. data/bin/pdfsh +4 -5
  28. data/bin/pdfwalker +1 -2
  29. data/bin/shell/.irbrc +45 -82
  30. data/bin/shell/console.rb +105 -130
  31. data/bin/shell/hexdump.rb +40 -64
  32. data/examples/README.md +34 -0
  33. data/examples/attachments/attachment.rb +38 -0
  34. data/examples/attachments/nested_document.rb +51 -0
  35. data/examples/encryption/encryption.rb +28 -0
  36. data/{samples/actions/triggerevents/trigger.rb → examples/events/events.rb} +13 -16
  37. data/examples/flash/flash.rb +37 -0
  38. data/{samples → examples}/flash/helloworld.swf +0 -0
  39. data/examples/forms/javascript.rb +54 -0
  40. data/examples/forms/xfa.rb +115 -0
  41. data/examples/javascript/hello_world.rb +22 -0
  42. data/examples/javascript/js_emulation.rb +54 -0
  43. data/examples/loop/goto.rb +32 -0
  44. data/examples/loop/named.rb +33 -0
  45. data/examples/signature/signature.rb +65 -0
  46. data/examples/uri/javascript.rb +56 -0
  47. data/examples/uri/open-uri.rb +21 -0
  48. data/examples/uri/submitform.rb +47 -0
  49. data/lib/origami.rb +29 -42
  50. data/lib/origami/3d.rb +350 -225
  51. data/lib/origami/acroform.rb +262 -288
  52. data/lib/origami/actions.rb +268 -288
  53. data/lib/origami/annotations.rb +697 -722
  54. data/lib/origami/array.rb +258 -184
  55. data/lib/origami/boolean.rb +74 -84
  56. data/lib/origami/catalog.rb +397 -434
  57. data/lib/origami/collections.rb +144 -0
  58. data/lib/origami/destinations.rb +233 -194
  59. data/lib/origami/dictionary.rb +253 -232
  60. data/lib/origami/encryption.rb +1274 -1243
  61. data/lib/origami/export.rb +232 -268
  62. data/lib/origami/extensions/fdf.rb +307 -220
  63. data/lib/origami/extensions/ppklite.rb +368 -435
  64. data/lib/origami/filespec.rb +197 -0
  65. data/lib/origami/filters.rb +301 -295
  66. data/lib/origami/filters/ascii.rb +177 -180
  67. data/lib/origami/filters/ccitt.rb +528 -535
  68. data/lib/origami/filters/crypt.rb +26 -35
  69. data/lib/origami/filters/dct.rb +46 -52
  70. data/lib/origami/filters/flate.rb +95 -94
  71. data/lib/origami/filters/jbig2.rb +49 -55
  72. data/lib/origami/filters/jpx.rb +38 -44
  73. data/lib/origami/filters/lzw.rb +189 -183
  74. data/lib/origami/filters/predictors.rb +221 -235
  75. data/lib/origami/filters/runlength.rb +103 -104
  76. data/lib/origami/font.rb +173 -186
  77. data/lib/origami/functions.rb +67 -81
  78. data/lib/origami/graphics.rb +25 -21
  79. data/lib/origami/graphics/colors.rb +178 -187
  80. data/lib/origami/graphics/instruction.rb +79 -85
  81. data/lib/origami/graphics/path.rb +142 -148
  82. data/lib/origami/graphics/patterns.rb +160 -167
  83. data/lib/origami/graphics/render.rb +43 -50
  84. data/lib/origami/graphics/state.rb +138 -153
  85. data/lib/origami/graphics/text.rb +188 -205
  86. data/lib/origami/graphics/xobject.rb +819 -815
  87. data/lib/origami/header.rb +63 -78
  88. data/lib/origami/javascript.rb +596 -597
  89. data/lib/origami/linearization.rb +285 -290
  90. data/lib/origami/metadata.rb +139 -148
  91. data/lib/origami/name.rb +112 -148
  92. data/lib/origami/null.rb +53 -62
  93. data/lib/origami/numeric.rb +162 -175
  94. data/lib/origami/obfuscation.rb +186 -174
  95. data/lib/origami/object.rb +593 -573
  96. data/lib/origami/outline.rb +42 -47
  97. data/lib/origami/outputintents.rb +73 -82
  98. data/lib/origami/page.rb +703 -592
  99. data/lib/origami/parser.rb +238 -290
  100. data/lib/origami/parsers/fdf.rb +41 -33
  101. data/lib/origami/parsers/pdf.rb +75 -95
  102. data/lib/origami/parsers/pdf/lazy.rb +137 -0
  103. data/lib/origami/parsers/pdf/linear.rb +64 -66
  104. data/lib/origami/parsers/ppklite.rb +34 -70
  105. data/lib/origami/pdf.rb +1030 -1005
  106. data/lib/origami/reference.rb +102 -102
  107. data/lib/origami/signature.rb +591 -609
  108. data/lib/origami/stream.rb +668 -551
  109. data/lib/origami/string.rb +397 -373
  110. data/lib/origami/template/patterns.rb +56 -0
  111. data/lib/origami/template/widgets.rb +151 -0
  112. data/lib/origami/trailer.rb +144 -158
  113. data/lib/origami/tree.rb +62 -0
  114. data/lib/origami/version.rb +23 -0
  115. data/lib/origami/webcapture.rb +88 -79
  116. data/lib/origami/xfa.rb +2863 -2882
  117. data/lib/origami/xreftable.rb +472 -384
  118. data/test/dataset/calc.pdf +85 -0
  119. data/test/dataset/crypto.pdf +82 -0
  120. data/test/dataset/empty.pdf +49 -0
  121. data/test/test_actions.rb +27 -0
  122. data/test/test_annotations.rb +90 -0
  123. data/test/test_pages.rb +31 -0
  124. data/test/test_pdf.rb +16 -0
  125. data/test/test_pdf_attachment.rb +34 -0
  126. data/test/test_pdf_create.rb +24 -0
  127. data/test/test_pdf_encrypt.rb +95 -0
  128. data/test/test_pdf_parse.rb +96 -0
  129. data/test/test_pdf_sign.rb +58 -0
  130. data/test/test_streams.rb +182 -0
  131. data/test/test_xrefs.rb +67 -0
  132. metadata +88 -58
  133. data/README +0 -67
  134. data/bin/pdf2graph +0 -121
  135. data/bin/pdfcocoon +0 -104
  136. data/lib/origami/file.rb +0 -233
  137. data/samples/README.txt +0 -45
  138. data/samples/actions/launch/calc.rb +0 -87
  139. data/samples/actions/launch/winparams.rb +0 -22
  140. data/samples/actions/loop/loopgoto.rb +0 -24
  141. data/samples/actions/loop/loopnamed.rb +0 -21
  142. data/samples/actions/named/named.rb +0 -31
  143. data/samples/actions/samba/smbrelay.rb +0 -26
  144. data/samples/actions/webbug/submitform.js +0 -26
  145. data/samples/actions/webbug/webbug-browser.rb +0 -68
  146. data/samples/actions/webbug/webbug-js.rb +0 -67
  147. data/samples/actions/webbug/webbug-reader.rb +0 -90
  148. data/samples/attachments/attach.rb +0 -40
  149. data/samples/attachments/attached.txt +0 -1
  150. data/samples/crypto/crypto.rb +0 -28
  151. data/samples/digsig/signed.rb +0 -46
  152. data/samples/exploits/cve-2008-2992-utilprintf.rb +0 -87
  153. data/samples/exploits/cve-2009-0927-geticon.rb +0 -65
  154. data/samples/exploits/exploit_customdictopen.rb +0 -55
  155. data/samples/exploits/getannots.rb +0 -69
  156. data/samples/flash/flash.rb +0 -31
  157. data/samples/javascript/attached.txt +0 -1
  158. data/samples/javascript/js.rb +0 -52
  159. data/templates/patterns.rb +0 -66
  160. data/templates/widgets.rb +0 -173
  161. data/templates/xdp.rb +0 -92
  162. data/test/ts_pdf.rb +0 -50
@@ -1,116 +1,116 @@
1
1
  =begin
2
2
 
3
- = File
4
- reference.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume Delugré <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- Origami is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU Lesser General Public License as published by
13
- the Free Software Foundation, either version 3 of the License, or
14
- (at your option) any later version.
15
-
16
- Origami is distributed in the hope that it will be useful,
17
- but WITHOUT ANY WARRANTY; without even the implied warranty of
18
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
- GNU Lesser General Public License for more details.
20
-
21
- You should have received a copy of the GNU Lesser General Public License
22
- along with Origami. If not, see <http://www.gnu.org/licenses/>.
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
18
 
24
19
  =end
25
20
 
26
21
  module Origami
27
22
 
28
- class InvalidReferenceError < Exception #:nodoc:
29
- end
30
-
31
- #
32
- # Class representing a Reference Object.
33
- # Reference are like symbolic links pointing to a particular object into the file.
34
- #
35
- class Reference
36
-
37
- include Origami::Object
38
-
39
- TOKENS = [ "(\\d+)" + WHITESPACES + "(\\d+)" + WHITESPACES + "R" ] #:nodoc:
40
- REGEXP_TOKEN = Regexp.new(TOKENS.first, Regexp::MULTILINE)
41
- @@regexp = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
42
-
43
- attr_accessor :refno, :refgen
44
-
45
- def initialize(refno, refgen)
46
- @refno, @refgen = refno, refgen
47
- end
48
-
49
- def self.parse(stream, parser = nil) #:nodoc:
50
-
51
- offset = stream.pos
52
-
53
- if stream.scan(@@regexp).nil?
54
- raise InvalidReferenceError, "Bad reference to indirect objet format"
55
- end
56
-
57
- refno = stream[2].to_i
58
- refgen = stream[4].to_i
59
-
60
- ref = Reference.new(refno,refgen)
61
- ref.file_offset = offset
62
-
63
- ref
64
- end
65
-
66
- def solve
67
- pdfdoc = self.pdf
68
-
69
- if pdfdoc.nil?
70
- raise InvalidReferenceError, "Not attached to any PDF"
71
- end
72
-
73
- target = pdfdoc.get_object(self)
74
-
75
- if target.nil? and not Origami::OPTIONS[:ignore_bad_references]
76
- raise InvalidReferenceError, "Cannot resolve reference : #{self.to_s}"
77
- end
78
-
79
- target or Null.new
80
- end
81
-
82
- def eql?(ref) #:nodoc
83
- ref.is_a?(Reference) and ref.refno == @refno and ref.refgen == @refgen
84
- end
85
-
86
- def hash #:nodoc:
87
- self.to_a.hash
88
- end
89
-
90
- def <=>(ref) #:nodoc
91
- self.to_a <=> ref.to_a
92
- end
93
-
94
- #
95
- # Returns a Ruby array with the object number and the generation this reference is pointing to.
96
- #
97
- def to_a
98
- [@refno, @refgen]
99
- end
100
-
101
- def to_s #:nodoc:
102
- super("#{@refno} #{@refgen} R")
23
+ class InvalidReferenceError < Error #:nodoc:
103
24
  end
104
-
25
+
105
26
  #
106
- # Returns self.
27
+ # Class representing a Reference Object.
28
+ # Reference are like symbolic links pointing to a particular object into the file.
107
29
  #
108
- def value
109
- self
110
- end
30
+ class Reference
31
+ include Origami::Object
32
+
33
+ TOKENS = [ "(?<no>\\d+)" + WHITESPACES + "(?<gen>\\d+)" + WHITESPACES + "R" ] #:nodoc:
34
+ REGEXP_TOKEN = Regexp.new(TOKENS.first, Regexp::MULTILINE)
35
+ @@regexp = Regexp.new(WHITESPACES + TOKENS.first + WHITESPACES)
36
+
37
+ attr_accessor :refno, :refgen
38
+
39
+ def initialize(refno, refgen)
40
+ super()
41
+
42
+ @refno, @refgen = refno, refgen
43
+ end
111
44
 
112
- def self.native_type ; Reference end
45
+ def self.parse(stream, parser = nil) #:nodoc:
46
+ offset = stream.pos
113
47
 
114
- end
48
+ if stream.scan(@@regexp).nil?
49
+ raise InvalidReferenceError, "Bad reference to indirect objet format"
50
+ end
51
+
52
+ no = stream['no'].to_i
53
+ gen = stream['gen'].to_i
54
+
55
+ ref = Reference.new(no, gen)
56
+ ref.file_offset = offset
57
+
58
+ ref
59
+ end
60
+
61
+ def solve
62
+ doc = self.document
63
+
64
+ if doc.nil?
65
+ raise InvalidReferenceError, "Not attached to any document"
66
+ end
67
+
68
+ target = doc.get_object(self)
69
+
70
+ if target.nil? and not Origami::OPTIONS[:ignore_bad_references]
71
+ raise InvalidReferenceError, "Cannot resolve reference : #{self.to_s}"
72
+ end
73
+
74
+ target or Null.new
75
+ end
76
+
77
+ def hash #:nodoc:
78
+ self.to_a.hash
79
+ end
80
+
81
+ def <=>(ref) #:nodoc
82
+ self.to_a <=> ref.to_a
83
+ end
84
+
85
+ #
86
+ # Compares to Reference object.
87
+ #
88
+ def ==(ref)
89
+ return false unless ref.is_a?(Reference)
90
+
91
+ self.to_a == ref.to_a
92
+ end
93
+ alias eql? ==
94
+
95
+ #
96
+ # Returns a Ruby array with the object number and the generation this reference is pointing to.
97
+ #
98
+ def to_a
99
+ [@refno, @refgen]
100
+ end
101
+
102
+ def to_s #:nodoc:
103
+ super("#{@refno} #{@refgen} R")
104
+ end
105
+
106
+ #
107
+ # Returns self.
108
+ #
109
+ def value
110
+ self
111
+ end
112
+
113
+ def self.native_type ; Reference end
114
+ end
115
115
 
116
116
  end
@@ -1,643 +1,625 @@
1
1
  =begin
2
2
 
3
- = File
4
- signature.rb
5
-
6
- = Info
7
- This file is part of Origami, PDF manipulation framework for Ruby
8
- Copyright (C) 2010 Guillaume Delugré <guillaume AT security-labs DOT org>
9
- All right reserved.
10
-
11
- Origami is free software: you can redistribute it and/or modify
12
- it under the terms of the GNU Lesser General Public License as published by
13
- the Free Software Foundation, either version 3 of the License, or
14
- (at your option) any later version.
15
-
16
- Origami is distributed in the hope that it will be useful,
17
- but WITHOUT ANY WARRANTY; without even the implied warranty of
18
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
- GNU Lesser General Public License for more details.
20
-
21
- You should have received a copy of the GNU Lesser General Public License
22
- along with Origami. If not, see <http://www.gnu.org/licenses/>.
3
+ This file is part of Origami, PDF manipulation framework for Ruby
4
+ Copyright (C) 2016 Guillaume Delugré.
5
+
6
+ Origami is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Lesser General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Origami is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Lesser General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Lesser General Public License
17
+ along with Origami. If not, see <http://www.gnu.org/licenses/>.
23
18
 
24
19
  =end
25
20
 
26
21
  begin
27
- require 'openssl' if Origami::OPTIONS[:use_openssl]
22
+ require 'openssl' if Origami::OPTIONS[:use_openssl]
28
23
  rescue LoadError
29
- Origami::OPTIONS[:use_openssl] = false
30
- end
24
+ Origami::OPTIONS[:use_openssl] = false
25
+ end
31
26
 
32
27
  require 'digest/sha1'
33
28
 
34
29
  module Origami
35
30
 
36
- class PDF
31
+ class PDF
32
+ class SignatureError < Error #:nodoc:
33
+ end
37
34
 
38
- class SignatureError < Exception #:nodoc:
39
- end
35
+ #
36
+ # Verify a document signature.
37
+ # _:trusted_certs_: an array of trusted X509 certificates.
38
+ # If no argument is passed, embedded certificates are treated as trusted.
39
+ #
40
+ def verify(trusted_certs: [])
41
+ unless Origami::OPTIONS[:use_openssl]
42
+ fail "OpenSSL is not present or has been disabled."
43
+ end
44
+
45
+ digsig = self.signature
46
+
47
+ unless digsig[:Contents].is_a?(String)
48
+ raise SignatureError, "Invalid digital signature contents"
49
+ end
50
+
51
+ store = OpenSSL::X509::Store.new
52
+ trusted_certs.each do |ca| store.add_cert(ca) end
53
+ flags = 0
54
+ flags |= OpenSSL::PKCS7::NOVERIFY if trusted_certs.empty?
55
+
56
+ stream = StringScanner.new(self.original_data)
57
+ stream.pos = digsig[:Contents].file_offset
58
+ Object.typeof(stream).parse(stream)
59
+ endofsig_offset = stream.pos
60
+ stream.terminate
61
+
62
+ s1,l1,s2,l2 = digsig.ByteRange
63
+ if s1.value != 0 or
64
+ (s2.value + l2.value) != self.original_data.size or
65
+ (s1.value + l1.value) != digsig[:Contents].file_offset or
66
+ s2.value != endofsig_offset
67
+
68
+ raise SignatureError, "Invalid signature byte range"
69
+ end
70
+
71
+ data = self.original_data[s1,l1] + self.original_data[s2,l2]
72
+
73
+ case digsig.SubFilter.value.to_s
74
+ when 'adbe.pkcs7.detached'
75
+ flags |= OpenSSL::PKCS7::DETACHED
76
+ p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
77
+ raise SignatureError, "Not a PKCS7 detached signature" unless p7.detached?
78
+ p7.verify([], store, data, flags)
79
+
80
+ when 'adbe.pkcs7.sha1'
81
+ p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
82
+ p7.verify([], store, nil, flags) and p7.data == Digest::SHA1.digest(data)
83
+
84
+ else
85
+ raise NotImplementedError, "Unsupported method #{digsig.SubFilter}"
86
+ end
87
+ end
40
88
 
41
- #
42
- # Verify a document signature.
43
- # Options:
44
- # _:trusted_: an array of trusted X509 certificates.
45
- # If no argument is passed, embedded certificates are treated as trusted.
46
- #
47
- def verify(options = {})
48
-
49
- unless Origami::OPTIONS[:use_openssl]
50
- fail "OpenSSL is not present or has been disabled."
51
- end
52
-
53
- params =
54
- {
55
- :trusted => []
56
- }.update(options)
57
-
58
- digsig = self.signature
59
-
60
- unless digsig[:Contents].is_a?(String)
61
- raise SignatureError, "Invalid digital signature contents"
62
- end
63
-
64
- store = OpenSSL::X509::Store.new
65
- params[:trusted].each do |ca| store.add_cert(ca) end
66
- flags = 0
67
- flags |= OpenSSL::PKCS7::NOVERIFY if params[:trusted].empty?
68
-
69
- stream = StringScanner.new(self.original_data)
70
- stream.pos = digsig[:Contents].file_offset
71
- Object.typeof(stream).parse(stream)
72
- endofsig_offset = stream.pos
73
- stream.terminate
74
-
75
- s1,l1,s2,l2 = digsig.ByteRange
76
- if s1.value != 0 or
77
- (s2.value + l2.value) != self.original_data.size or
78
- (s1.value + l1.value) != digsig[:Contents].file_offset or
79
- s2.value != endofsig_offset
80
-
81
- raise SignatureError, "Invalid signature byte range"
82
- end
83
-
84
- data = self.original_data[s1,l1] + self.original_data[s2,l2]
85
-
86
- case digsig.SubFilter.value.to_s
87
- when 'adbe.pkcs7.detached'
88
- flags |= OpenSSL::PKCS7::DETACHED
89
- p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
90
- raise SignatureError, "Not a PKCS7 detached signature" unless p7.detached?
91
- p7.verify([], store, data, flags)
92
-
93
- when 'adbe.pkcs7.sha1'
94
- p7 = OpenSSL::PKCS7.new(digsig[:Contents].value)
95
- p7.verify([], store, nil, flags) and p7.data == Digest::SHA1.digest(data)
96
-
97
- else
98
- raise NotImplementedError, "Unsupported method #{digsig.SubFilter}"
99
- end
100
- end
101
-
102
- #
103
- # Sign the document with the given key and x509 certificate.
104
- # _certificate_:: The X509 certificate containing the public key.
105
- # _key_:: The private key associated with the certificate.
106
- # _ca_:: Optional CA certificates used to sign the user certificate.
107
- #
108
- def sign(certificate, key, options = {})
109
-
110
- unless Origami::OPTIONS[:use_openssl]
111
- fail "OpenSSL is not present or has been disabled."
112
- end
113
-
114
- params =
115
- {
116
- :method => "adbe.pkcs7.detached",
117
- :ca => [],
118
- :annotation => nil,
119
- :location => nil,
120
- :contact => nil,
121
- :reason => nil
122
- }.update(options)
123
-
124
- unless certificate.is_a?(OpenSSL::X509::Certificate)
125
- raise TypeError, "A OpenSSL::X509::Certificate object must be passed."
126
- end
127
-
128
- unless key.is_a?(OpenSSL::PKey::RSA)
129
- raise TypeError, "A OpenSSL::PKey::RSA object must be passed."
130
- end
131
-
132
- ca = params[:ca]
133
- unless ca.is_a?(::Array)
134
- raise TypeError, "Expected an Array of CA certificate."
135
- end
136
-
137
- annotation = params[:annotation]
138
- unless annotation.nil? or annotation.is_a?(Annotation::Widget::Signature)
139
- raise TypeError, "Expected a Annotation::Widget::Signature object."
140
- end
141
-
142
- case params[:method]
143
- when 'adbe.pkcs7.detached'
144
- signfield_size = lambda{|crt,key,ca|
145
- datatest = "abcdefghijklmnopqrstuvwxyz"
146
- OpenSSL::PKCS7.sign(
147
- crt,
148
- key,
149
- datatest,
150
- ca,
151
- OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
152
- ).to_der.size + 128
153
- }
154
- when 'adbe.pkcs7.sha1'
155
- signfield_size = lambda{|crt,key,ca|
156
- datatest = "abcdefghijklmnopqrstuvwxyz"
157
- OpenSSL::PKCS7.sign(
158
- crt,
159
- key,
160
- Digest::SHA1.digest(datatest),
161
- ca,
162
- OpenSSL::PKCS7::BINARY
163
- ).to_der.size + 128
164
- }
165
-
166
- when 'adbe.x509.rsa_sha1'
167
- signfield_size = lambda{|crt,key,ca|
168
- datatest = "abcdefghijklmnopqrstuvwxyz"
169
- key.private_encrypt(
170
- Digest::SHA1.digest(datatest)
171
- ).size + 128
172
- }
173
- raise NotImplementedError, "Unsupported method #{params[:method].inspect}"
174
-
175
- else
176
- raise NotImplementedError, "Unsupported method #{params[:method].inspect}"
177
- end
178
-
179
- digsig = Signature::DigitalSignature.new.set_indirect(true)
180
-
181
- if annotation.nil?
182
- annotation = Annotation::Widget::Signature.new
183
- annotation.Rect = Rectangle[:llx => 0.0, :lly => 0.0, :urx => 0.0, :ury => 0.0]
184
- end
185
-
186
- annotation.V = digsig
187
- add_fields(annotation)
188
- self.Catalog.AcroForm.SigFlags =
189
- InteractiveForm::SigFlags::SIGNATURESEXIST | InteractiveForm::SigFlags::APPENDONLY
190
-
191
- digsig.Type = :Sig #:nodoc:
192
- digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, ca]) #:nodoc:
193
- digsig.Filter = Name.new("Adobe.PPKMS") #:nodoc:
194
- digsig.SubFilter = Name.new(params[:method]) #:nodoc:
195
- digsig.ByteRange = [0, 0, 0, 0] #:nodoc:
196
-
197
- digsig.Location = HexaString.new(params[:location]) if params[:location]
198
- digsig.ContactInfo = HexaString.new(params[:contact]) if params[:contact]
199
- digsig.Reason = HexaString.new(params[:reason]) if params[:reason]
200
-
201
- if params[:method] == 'adbe.x509.rsa_sha1'
202
- digsig.Cert =
203
- if ca.empty?
204
- HexaString.new(certificate.to_der)
205
- else
206
- [ HexaString.new(certificate.to_der) ] + ca.map{ |crt| HexaString.new(crt.to_der) }
207
- end
208
- end
209
-
210
- #
211
- # Flattening the PDF to get file view.
212
- #
213
- compile
214
-
215
- #
216
- # Creating an empty Xref table to compute signature byte range.
217
- #
218
- rebuild_dummy_xrefs
219
-
220
- sigoffset = get_object_offset(digsig.no, digsig.generation) + digsig.sigOffset
221
-
222
- digsig.ByteRange[0] = 0
223
- digsig.ByteRange[1] = sigoffset
224
- digsig.ByteRange[2] = sigoffset + digsig.Contents.size
225
-
226
- digsig.ByteRange[3] = filesize - digsig.ByteRange[2] until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
227
-
228
- # From that point the file size remains constant
229
-
230
- #
231
- # Correct Xrefs variations caused by ByteRange modifications.
232
- #
233
- rebuildxrefs
234
-
235
- filedata = output()
236
- signable_data = filedata[digsig.ByteRange[0],digsig.ByteRange[1]] + filedata[digsig.ByteRange[2],digsig.ByteRange[3]]
237
-
238
- signature =
239
- case params[:method]
240
- when 'adbe.pkcs7.detached'
241
- OpenSSL::PKCS7.sign(
242
- certificate,
243
- key,
244
- signable_data,
245
- ca,
246
- OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
247
- ).to_der
89
+ #
90
+ # Sign the document with the given key and x509 certificate.
91
+ # _certificate_:: The X509 certificate containing the public key.
92
+ # _key_:: The private key associated with the certificate.
93
+ # _method_:: The PDF signature identifier.
94
+ # _ca_:: Optional CA certificates used to sign the user certificate.
95
+ # _annotation_:: Annotation associated with the signature.
96
+ # _issuer_:: Issuer name.
97
+ # _location_:: Signature location.
98
+ # _contact_:: Signer contact.
99
+ # _reason_:: Signing reason.
100
+ #
101
+ def sign(certificate, key,
102
+ method: "adbe.pkcs7.detached",
103
+ ca: [],
104
+ annotation: nil,
105
+ issuer: nil,
106
+ location: nil,
107
+ contact: nil,
108
+ reason: nil)
109
+
110
+ unless Origami::OPTIONS[:use_openssl]
111
+ fail "OpenSSL is not present or has been disabled."
112
+ end
113
+
114
+ unless certificate.is_a?(OpenSSL::X509::Certificate)
115
+ raise TypeError, "A OpenSSL::X509::Certificate object must be passed."
116
+ end
117
+
118
+ unless key.is_a?(OpenSSL::PKey::RSA)
119
+ raise TypeError, "A OpenSSL::PKey::RSA object must be passed."
120
+ end
121
+
122
+ unless ca.is_a?(::Array)
123
+ raise TypeError, "Expected an Array of CA certificate."
124
+ end
125
+
126
+ unless annotation.nil? or annotation.is_a?(Annotation::Widget::Signature)
127
+ raise TypeError, "Expected a Annotation::Widget::Signature object."
128
+ end
129
+
130
+ case method
131
+ when 'adbe.pkcs7.detached'
132
+ signfield_size = -> (crt, pkey, certs) do
133
+ OpenSSL::PKCS7.sign(
134
+ crt,
135
+ pkey,
136
+ "",
137
+ certs,
138
+ OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
139
+ ).to_der.size
140
+ end
141
+
142
+ when 'adbe.pkcs7.sha1'
143
+ signfield_size = -> (crt, pkey, certs) do
144
+ OpenSSL::PKCS7.sign(
145
+ crt,
146
+ pkey,
147
+ Digest::SHA1.digest(''),
148
+ certs,
149
+ OpenSSL::PKCS7::BINARY
150
+ ).to_der.size
151
+ end
152
+
153
+ when 'adbe.x509.rsa_sha1'
154
+ signfield_size = -> (crt, pkey, certs) do
155
+ pkey.private_encrypt(
156
+ Digest::SHA1.digest('')
157
+ ).size
158
+ end
159
+ raise NotImplementedError, "Unsupported method #{method.inspect}"
160
+
161
+ else
162
+ raise NotImplementedError, "Unsupported method #{method.inspect}"
163
+ end
164
+
165
+ digsig = Signature::DigitalSignature.new.set_indirect(true)
166
+
167
+ if annotation.nil?
168
+ annotation = Annotation::Widget::Signature.new
169
+ annotation.Rect = Rectangle[:llx => 0.0, :lly => 0.0, :urx => 0.0, :ury => 0.0]
170
+ end
171
+
172
+ annotation.V = digsig
173
+ add_fields(annotation)
174
+ self.Catalog.AcroForm.SigFlags =
175
+ InteractiveForm::SigFlags::SIGNATURESEXIST | InteractiveForm::SigFlags::APPENDONLY
176
+
177
+ digsig.Type = :Sig #:nodoc:
178
+ digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, ca]) #:nodoc:
179
+ digsig.Filter = :"Adobe.PPKLite" #:nodoc:
180
+ digsig.SubFilter = Name.new(method) #:nodoc:
181
+ digsig.ByteRange = [0, 0, 0, 0] #:nodoc:
182
+ digsig.Name = issuer
183
+
184
+ digsig.Location = HexaString.new(location) if location
185
+ digsig.ContactInfo = HexaString.new(contact) if contact
186
+ digsig.Reason = HexaString.new(reason) if reason
187
+
188
+ if method == 'adbe.x509.rsa_sha1'
189
+ digsig.Cert =
190
+ if ca.empty?
191
+ HexaString.new(certificate.to_der)
192
+ else
193
+ [ HexaString.new(certificate.to_der) ] + ca.map{ |crt| HexaString.new(crt.to_der) }
194
+ end
195
+ end
196
+
197
+ #
198
+ # Flattening the PDF to get file view.
199
+ #
200
+ compile
201
+
202
+ #
203
+ # Creating an empty Xref table to compute signature byte range.
204
+ #
205
+ rebuild_dummy_xrefs
206
+
207
+ sig_offset = get_object_offset(digsig.no, digsig.generation) + digsig.signature_offset
208
+
209
+ digsig.ByteRange[0] = 0
210
+ digsig.ByteRange[1] = sig_offset
211
+ digsig.ByteRange[2] = sig_offset + digsig.Contents.to_s.bytesize
212
+
213
+ until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
214
+ digsig.ByteRange[3] = filesize - digsig.ByteRange[2]
215
+ end
216
+
217
+ # From that point on, the file size remains constant
218
+
219
+ #
220
+ # Correct Xrefs variations caused by ByteRange modifications.
221
+ #
222
+ rebuild_xrefs
223
+
224
+ file_data = output()
225
+ signable_data = file_data[digsig.ByteRange[0],digsig.ByteRange[1]] +
226
+ file_data[digsig.ByteRange[2],digsig.ByteRange[3]]
227
+
228
+ signature =
229
+ case method
230
+ when 'adbe.pkcs7.detached'
231
+ OpenSSL::PKCS7.sign(
232
+ certificate,
233
+ key,
234
+ signable_data,
235
+ ca,
236
+ OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
237
+ ).to_der
238
+
239
+ when 'adbe.pkcs7.sha1'
240
+ OpenSSL::PKCS7.sign(
241
+ certificate,
242
+ key,
243
+ Digest::SHA1.digest(signable_data),
244
+ ca,
245
+ OpenSSL::PKCS7::BINARY
246
+ ).to_der
247
+
248
+ when 'adbe.x509.rsa_sha1'
249
+ key.private_encrypt(Digest::SHA1.digest(signable_data))
250
+ end
251
+
252
+ digsig.Contents[0, signature.size] = signature
253
+
254
+ #
255
+ # No more modification are allowed after signing.
256
+ #
257
+ self.freeze
258
+ end
259
+
260
+ #
261
+ # Returns whether the document contains a digital signature.
262
+ #
263
+ def signed?
264
+ begin
265
+ self.Catalog.AcroForm.is_a?(Dictionary) and
266
+ self.Catalog.AcroForm.has_key?(:SigFlags) and
267
+ (self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::SIGNATURESEXIST != 0)
268
+ rescue InvalidReferenceError
269
+ false
270
+ end
271
+ end
248
272
 
249
- when 'adbe.pkcs7.sha1'
250
- OpenSSL::PKCS7.sign(
251
- certificate,
252
- key,
253
- Digest::SHA1.digest(signable_data),
254
- ca,
255
- OpenSSL::PKCS7::BINARY
273
+ #
274
+ # Enable the document Usage Rights.
275
+ # _rights_:: list of rights defined in UsageRights::Rights
276
+ #
277
+ def enable_usage_rights(cert, pkey, *rights)
278
+ unless Origami::OPTIONS[:use_openssl]
279
+ fail "OpenSSL is not present or has been disabled."
280
+ end
281
+
282
+ signfield_size = -> (crt, key, ca) do
283
+ OpenSSL::PKCS7.sign(
284
+ crt,
285
+ key,
286
+ '',
287
+ ca,
288
+ OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
289
+ ).to_der.size
290
+ end
291
+
292
+ #
293
+ # Load key pair
294
+ #
295
+ key = pkey.is_a?(OpenSSL::PKey::RSA) ? pkey : OpenSSL::PKey::RSA.new(pkey)
296
+ certificate = cert.is_a?(OpenSSL::X509::Certificate) ? cert : OpenSSL::X509::Certificate.new(cert)
297
+
298
+ #
299
+ # Forge digital signature dictionary
300
+ #
301
+ digsig = Signature::DigitalSignature.new.set_indirect(true)
302
+
303
+ self.Catalog.AcroForm ||= InteractiveForm.new
304
+ #self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::APPENDONLY
305
+
306
+ digsig.Type = :Sig #:nodoc:
307
+ digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, []]) #:nodoc:
308
+ digsig.Filter = :"Adobe.PPKLite" #:nodoc:
309
+ digsig.Name = "ARE Acrobat Product v8.0 P23 0002337" #:nodoc:
310
+ digsig.SubFilter = :"adbe.pkcs7.detached" #:nodoc:
311
+ digsig.ByteRange = [0, 0, 0, 0] #:nodoc:
312
+
313
+ sigref = Signature::Reference.new #:nodoc:
314
+ sigref.Type = :SigRef #:nodoc:
315
+ sigref.TransformMethod = :UR3 #:nodoc:
316
+ sigref.Data = self.Catalog
317
+
318
+ sigref.TransformParams = UsageRights::TransformParams.new
319
+ sigref.TransformParams.P = true #:nodoc:
320
+ sigref.TransformParams.Type = :TransformParams #:nodoc:
321
+ sigref.TransformParams.V = UsageRights::TransformParams::VERSION
322
+
323
+ rights.each do |right|
324
+ sigref.TransformParams[right.first] ||= []
325
+ sigref.TransformParams[right.first].concat(right[1..-1])
326
+ end
327
+
328
+ digsig.Reference = [ sigref ]
329
+
330
+ self.Catalog.Perms ||= Perms.new
331
+ self.Catalog.Perms.UR3 = digsig
332
+
333
+ #
334
+ # Flattening the PDF to get file view.
335
+ #
336
+ compile
337
+
338
+ #
339
+ # Creating an empty Xref table to compute signature byte range.
340
+ #
341
+ rebuild_dummy_xrefs
342
+
343
+ sig_offset = get_object_offset(digsig.no, digsig.generation) + digsig.signature_offset
344
+
345
+ digsig.ByteRange[0] = 0
346
+ digsig.ByteRange[1] = sig_offset
347
+ digsig.ByteRange[2] = sig_offset + digsig.Contents.size
348
+
349
+ until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
350
+ digsig.ByteRange[3] = filesize - digsig.ByteRange[2]
351
+ end
352
+
353
+ # From that point on, the file size remains constant
354
+
355
+ #
356
+ # Correct Xrefs variations caused by ByteRange modifications.
357
+ #
358
+ rebuild_xrefs
359
+
360
+ file_data = output()
361
+ signable_data = file_data[digsig.ByteRange[0],digsig.ByteRange[1]] +
362
+ file_data[digsig.ByteRange[2],digsig.ByteRange[3]]
363
+
364
+ signature = OpenSSL::PKCS7.sign(
365
+ certificate,
366
+ key,
367
+ signable_data,
368
+ [],
369
+ OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY
256
370
  ).to_der
371
+ digsig.Contents[0, signature.size] = signature
257
372
 
258
- when 'adbe.x509.rsa_sha1'
259
- key.private_encrypt(Digest::SHA1.digest(signable_data))
373
+ #
374
+ # No more modification are allowed after signing.
375
+ #
376
+ self.freeze
260
377
  end
261
378
 
262
- digsig.Contents[0, signature.size] = signature
263
-
264
- #
265
- # No more modification are allowed after signing.
266
- #
267
- self.freeze
268
- end
269
-
270
- #
271
- # Returns whether the document contains a digital signature.
272
- #
273
- def is_signed?
274
- begin
275
- self.Catalog.AcroForm.is_a?(Dictionary) and
276
- self.Catalog.AcroForm.has_key?(:SigFlags) and
277
- (self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::SIGNATURESEXIST != 0)
278
- rescue InvalidReferenceError
279
- false
280
- end
281
- end
282
-
283
- #
284
- # Enable the document Usage Rights.
285
- # _rights_:: list of rights defined in UsageRights::Rights
286
- #
287
- def enable_usage_rights(cert, pkey, *rights)
288
-
289
- unless Origami::OPTIONS[:use_openssl]
290
- fail "OpenSSL is not present or has been disabled."
291
- end
292
-
293
- signfield_size = lambda{|crt, key, ca|
294
- datatest = "abcdefghijklmnopqrstuvwxyz"
295
- OpenSSL::PKCS7.sign(crt, key, datatest, ca, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der.size + 128
296
- }
297
-
298
- #
299
- # Load key pair
300
- #
301
- key = pkey.is_a?(OpenSSL::PKey::RSA) ? pkey : OpenSSL::PKey::RSA.new(pkey)
302
- certificate = cert.is_a?(OpenSSL::X509::Certificate) ? cert : OpenSSL::X509::Certificate.new(cert)
303
-
304
- #
305
- # Forge digital signature dictionary
306
- #
307
- digsig = Signature::DigitalSignature.new.set_indirect(true)
308
-
309
- self.Catalog.AcroForm ||= InteractiveForm.new
310
- #self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::APPENDONLY
311
-
312
- digsig.Type = :Sig #:nodoc:
313
- digsig.Contents = HexaString.new("\x00" * signfield_size[certificate, key, []]) #:nodoc:
314
- digsig.Filter = Name.new("Adobe.PPKLite") #:nodoc:
315
- digsig.Name = "ARE Acrobat Product v8.0 P23 0002337" #:nodoc:
316
- digsig.SubFilter = Name.new("adbe.pkcs7.detached") #:nodoc:
317
- digsig.ByteRange = [0, 0, 0, 0] #:nodoc:
318
-
319
- sigref = Signature::Reference.new #:nodoc:
320
- sigref.Type = :SigRef #:nodoc:
321
- sigref.TransformMethod = :UR3 #:nodoc:
322
- sigref.Data = self.Catalog
323
-
324
- sigref.TransformParams = UsageRights::TransformParams.new
325
- sigref.TransformParams.P = true #:nodoc:
326
- sigref.TransformParams.Type = :TransformParams #:nodoc:
327
- sigref.TransformParams.V = UsageRights::TransformParams::VERSION
328
-
329
- rights.each do |right|
330
- sigref.TransformParams[right.first] ||= []
331
- sigref.TransformParams[right.first].concat(right[1..-1])
332
- end
333
-
334
- digsig.Reference = [ sigref ]
335
-
336
- self.Catalog.Perms ||= Perms.new
337
- self.Catalog.Perms.UR3 = digsig
338
-
339
- #
340
- # Flattening the PDF to get file view.
341
- #
342
- compile
343
-
344
- #
345
- # Creating an empty Xref table to compute signature byte range.
346
- #
347
- rebuild_dummy_xrefs
348
-
349
- sigoffset = get_object_offset(digsig.no, digsig.generation) + digsig.sigOffset
350
-
351
- digsig.ByteRange[0] = 0
352
- digsig.ByteRange[1] = sigoffset
353
- digsig.ByteRange[2] = sigoffset + digsig.Contents.size
354
-
355
- digsig.ByteRange[3] = filesize - digsig.ByteRange[2] until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
356
-
357
- # From that point the file size remains constant
358
-
359
- #
360
- # Correct Xrefs variations caused by ByteRange modifications.
361
- #
362
- rebuildxrefs
363
-
364
- filedata = output()
365
- signable_data = filedata[digsig.ByteRange[0],digsig.ByteRange[1]] + filedata[digsig.ByteRange[2],digsig.ByteRange[3]]
366
-
367
- signature = OpenSSL::PKCS7.sign(certificate, key, signable_data, [], OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
368
- digsig.Contents[0, signature.size] = signature
369
-
370
- #
371
- # No more modification are allowed after signing.
372
- #
373
- self.freeze
374
-
379
+ def usage_rights?
380
+ not self.Catalog.Perms.nil? and
381
+ (not self.Catalog.Perms.has_key?(:UR3) or not self.Catalog.Perms.has_key?(:UR))
382
+ end
383
+
384
+ def signature
385
+ raise SignatureError, "Not a signed document" unless self.signed?
386
+
387
+ self.each_field do |field|
388
+ return field.V if field.FT == :Sig and field.V.is_a?(Dictionary)
389
+ end
390
+
391
+ raise SignatureError, "Cannot find digital signature"
392
+ end
375
393
  end
376
-
377
- def has_usage_rights?
378
- not self.Catalog.Perms.nil? and (not self.Catalog.Perms.has_key?(:UR3) or not self.Catalog.Perms.has_key?(:UR))
394
+
395
+ class Perms < Dictionary
396
+ include StandardObject
397
+
398
+ field :DocMDP, :Type => Dictionary
399
+ field :UR, :Type => Dictionary
400
+ field :UR3, :Type => Dictionary, :Version => "1.6"
379
401
  end
380
402
 
381
- def signature
382
- raise SignatureError, "Not a signed document" unless self.is_signed?
403
+ module Signature
404
+
405
+ #
406
+ # Class representing a signature which can be embedded in DigitalSignature dictionary.
407
+ # It must be a direct object.
408
+ #
409
+ class Reference < Dictionary
410
+ include StandardObject
411
+
412
+ field :Type, :Type => Name, :Default => :SigRef
413
+ field :TransformMethod, :Type => Name, :Default => :DocMDP, :Required => true
414
+ field :TransformParams, :Type => Dictionary
415
+ field :Data, :Type => Object
416
+ field :DigestMethod, :Type => Name, :Default => :MD5
417
+ field :DigestValue, :Type => String
418
+ field :DigestLocation, :Type => Array
419
+
420
+ def initialize(hash = {}, parser = nil)
421
+ set_indirect(false)
383
422
 
384
- self.each_field do |field|
385
- if field.FT == :Sig and field.V.is_a?(Dictionary)
386
- return field.V
423
+ super(hash, parser)
424
+ end
387
425
  end
388
- end
389
426
 
390
- raise SignatureError, "Cannot find digital signature"
391
- end
427
+ class BuildData < Dictionary
428
+ include StandardObject
429
+
430
+ field :Name, :Type => Name, :Version => "1.5"
431
+ field :Date, :Type => String, :Version => "1.5"
432
+ field :R, :Type => Number, :Version => "1.5"
433
+ field :PreRelease, :Type => Boolean, :Default => false, :Version => "1.5"
434
+ field :OS, :Type => Array, :Version => "1.5"
435
+ field :NonEFontNoWarn, :Type => Boolean, :Version => "1.5"
436
+ field :TrustedMode, :Type => Boolean, :Version => "1.5"
437
+ field :V, :Type => Number, :Version => "1.5"
438
+
439
+ def initialize(hash = {}, parser = nil)
440
+ set_indirect(false)
392
441
 
393
- end
394
-
395
- class Perms < Dictionary
396
-
397
- include StandardObject
398
-
399
- field :DocMDP, :Type => Dictionary
400
- field :UR, :Type => Dictionary
401
- field :UR3, :Type => Dictionary, :Version => "1.6"
402
-
403
- end
404
-
405
- module Signature
406
-
407
- #
408
- # Class representing a digital signature.
409
- #
410
- class DigitalSignature < Dictionary
411
-
412
- include StandardObject
413
-
414
- field :Type, :Type => Name, :Default => :Sig
415
- field :Filter, :Type => Name, :Default => "Adobe.PPKMS".to_sym, :Required => true
416
- field :SubFilter, :Type => Name
417
- field :Contents, :Type => String, :Required => true
418
- field :Cert, :Type => [ Array, String ]
419
- field :ByteRange, :Type => Array
420
- field :Reference, :Type => Array, :Version => "1.5"
421
- field :Changes, :Type => Array
422
- field :Name, :Type => String
423
- field :M, :Type => String
424
- field :Location, :Type => String
425
- field :Reason, :Type => String
426
- field :ContactInfo, :Type => String
427
- field :R, :Type => Integer
428
- field :V, :Type => Integer, :Default => 0, :Version => "1.5"
429
- field :Prop_Build, :Type => Dictionary, :Version => "1.5"
430
- field :Prop_AuthTime, :Type => Integer, :Version => "1.5"
431
- field :Prop_AuthType, :Type => Name, :Version => "1.5"
432
-
433
- def pre_build #:nodoc:
434
- self.M = Origami::Date.now
435
- self.Prop_Build ||= BuildProperties.new.pre_build
436
-
437
- super
438
- end
439
-
440
- def to_s(dummy_param = nil) #:nodoc:
441
- indent = 1
442
- pairs = self.to_a
443
- content = TOKENS.first + EOL
444
-
445
- pairs.sort_by{ |k| k.to_s }.reverse.each do |pair|
446
- key, value = pair[0].to_o, pair[1].to_o
447
-
448
- content << "\t" * indent + key.to_s + " " + (value.is_a?(Dictionary) ? value.to_s(indent + 1) : value.to_s) + EOL
442
+ super(hash, parser)
443
+ end
449
444
  end
450
-
451
- content << "\t" * (indent - 1) + TOKENS.last
452
-
453
- output(content)
454
- end
455
-
456
- def sigOffset #:nodoc:
457
- base = 1
458
- pairs = self.to_a
459
- content = "#{no} #{generation} obj" + EOL + TOKENS.first + EOL
460
-
461
- pairs.sort_by{ |k| k.to_s }.reverse.each do |pair|
462
- key, value = pair[0].to_o, pair[1].to_o
463
-
464
- if key == :Contents
465
- content << "\t" * base + key.to_s + " "
466
-
467
- return content.size
468
- else
469
- content << "\t" * base + key.to_s + " " + (value.is_a?(Dictionary) ? value.to_s(base+1) : value.to_s) + EOL
470
- end
445
+
446
+ class AppData < BuildData
447
+ field :REx, :Type => String, :Version => "1.6"
448
+ end
449
+
450
+ class SigQData < BuildData
451
+ field :Preview, :Type => Boolean, :Default => false, :Version => "1.7"
452
+ end
453
+
454
+ class BuildProperties < Dictionary
455
+ include StandardObject
456
+
457
+ field :Filter, :Type => BuildData, :Version => "1.5"
458
+ field :PubSec, :Type => BuildData, :Version => "1.5"
459
+ field :App, :Type => AppData, :Version => "1.5"
460
+ field :SigQ, :Type => SigQData, :Version => "1.7"
461
+
462
+ def initialize(hash = {}, parser = nil)
463
+ set_indirect(false)
464
+
465
+ super(hash, parser)
466
+ end
467
+
468
+ def pre_build #:nodoc:
469
+ self.Filter ||= BuildData.new
470
+ self.Filter.Name ||= :"Adobe.PPKLite"
471
+ self.Filter.R ||= 0x20020
472
+ self.Filter.V ||= 2
473
+ self.Filter.Date ||= Time.now.to_s
474
+
475
+ self.PubSec ||= BuildData.new
476
+ self.PubSec.NonEFontNoWarn ||= true
477
+ self.PubSec.Date ||= Time.now.to_s
478
+ self.PubSec.R ||= 0x20021
479
+
480
+ self.App ||= AppData.new
481
+ self.App.Name ||= :Reader
482
+ self.App.REx = "11.0.8"
483
+ self.App.TrustedMode ||= true
484
+ self.App.OS ||= [ :Win ]
485
+ self.App.R ||= 0xb0008
486
+
487
+ super
488
+ end
489
+ end
490
+
491
+ #
492
+ # Class representing a digital signature.
493
+ #
494
+ class DigitalSignature < Dictionary
495
+ include StandardObject
496
+
497
+ field :Type, :Type => Name, :Default => :Sig
498
+ field :Filter, :Type => Name, :Default => :"Adobe.PPKLite", :Required => true
499
+ field :SubFilter, :Type => Name
500
+ field :Contents, :Type => String, :Required => true
501
+ field :Cert, :Type => [ String, Array.of(String) ]
502
+ field :ByteRange, :Type => Array.of(Integer, length: 4)
503
+ field :Reference, :Type => Array.of(Reference), :Version => "1.5"
504
+ field :Changes, :Type => Array
505
+ field :Name, :Type => String
506
+ field :M, :Type => String
507
+ field :Location, :Type => String
508
+ field :Reason, :Type => String
509
+ field :ContactInfo, :Type => String
510
+ field :R, :Type => Integer
511
+ field :V, :Type => Integer, :Default => 0, :Version => "1.5"
512
+ field :Prop_Build, :Type => BuildProperties, :Version => "1.5"
513
+ field :Prop_AuthTime, :Type => Integer, :Version => "1.5"
514
+ field :Prop_AuthType, :Type => Name, :Version => "1.5"
515
+
516
+ def pre_build #:nodoc:
517
+ self.M = Origami::Date.now
518
+ self.Prop_Build ||= BuildProperties.new.pre_build
519
+
520
+ super
521
+ end
522
+
523
+ def to_s(indent: 1, tab: "\t") #:nodoc:
524
+
525
+ # Must be deterministic.
526
+ indent, tab = 1, "\t"
527
+
528
+ content = TOKENS.first + EOL
529
+
530
+ self.to_a.sort_by{ |key, _| key }.reverse.each do |key, value|
531
+ content << tab * indent << key.to_s << " "
532
+ content << (value.is_a?(Dictionary) ? value.to_s(indent: indent + 1) : value.to_s) << EOL
533
+ end
534
+
535
+ content << tab * (indent - 1) << TOKENS.last
536
+
537
+ output(content)
538
+ end
539
+
540
+ def signature_offset #:nodoc:
541
+ indent, tab = 1, "\t"
542
+ content = "#{no} #{generation} obj" + EOL + TOKENS.first + EOL
543
+
544
+ self.to_a.sort_by{ |key, _| key }.reverse.each do |key, value|
545
+ if key == :Contents
546
+ content << tab * indent + key.to_s + " "
547
+
548
+ return content.size
549
+ else
550
+ content << tab * indent + key.to_s << " "
551
+ content << (value.is_a?(Dictionary) ? value.to_s(indent: indent + 1) : value.to_s) << EOL
552
+ end
553
+ end
554
+
555
+ nil
556
+ end
471
557
  end
472
-
473
- nil
474
- end
475
-
476
- end
477
-
478
- #
479
- # Class representing a signature which can be embedded in DigitalSignature dictionary.
480
- # It must be a direct object.
481
- #
482
- class Reference < Dictionary
483
-
484
- include StandardObject
485
-
486
- field :Type, :Type => Name, :Default => :SigRef
487
- field :TransformMethod, :Type => Name, :Default => :DocMDP, :Required => true
488
- field :TransformParams, :Type => Dictionary
489
- field :Data, :Type => Object
490
- field :DigestMethod, :Type => Name, :Default => :MD5
491
- field :DigestValue, :Type => String
492
- field :DigestLocation, :Type => Array
493
-
494
- def initialize(hash = {})
495
- set_indirect(false)
496
-
497
- super(hash)
498
- end
499
- end
500
-
501
- class BuildProperties < Dictionary
502
-
503
- include StandardObject
504
-
505
- field :Filter, :Type => Dictionary, :Version => "1.5"
506
- field :PubSec, :Type => Dictionary, :Version => "1.5"
507
- field :App, :Type => Dictionary, :Version => "1.5"
508
- field :SigQ, :Type => Dictionary, :Version => "1.7"
509
-
510
- def initialize(hash = {})
511
- set_indirect(false)
512
-
513
- super(hash)
514
- end
515
-
516
- def pre_build #:nodoc:
517
-
518
- self.Filter ||= BuildData.new
519
- self.Filter.Name ||= Name.new("Adobe.PPKMS")
520
- self.Filter.R ||= 0x2001D
521
- self.Filter.Date ||= Time.now.to_s
522
-
523
- self.SigQ ||= SigQData.new
524
- self.SigQ.Preview ||= false
525
- self.SigQ.R ||= 0x2001D
526
-
527
- self.PubSec ||= BuildData.new
528
- self.PubSec.NonEFontNoWarn ||= false
529
- self.PubSec.Date ||= Time.now.to_s
530
- self.PubSec.R ||= 0x2001D
531
-
532
- self.App ||= AppData.new
533
- self.App.TrustedMode ||= false
534
- self.App.OS ||= [ :Win ]
535
- self.App.R ||= 0x70000
536
- self.App.Name ||= Name.new("Exchange-Pro")
537
-
538
- super
539
- end
540
-
541
- end
542
-
543
- class BuildData < Dictionary
544
-
545
- include StandardObject
546
-
547
- field :Name, :Type => Name, :Version => "1.5"
548
- field :Date, :Type => String, :Version => "1.5"
549
- field :R, :Type => Number, :Version => "1.5"
550
- field :PreRelease, :Type => Boolean, :Default => false, :Version => "1.5"
551
- field :OS, :Type => Array, :Version => "1.5"
552
- field :NonEFontNoWarn, :Type => Boolean, :Version => "1.5"
553
- field :TrustedMode, :Type => Boolean, :Version => "1.5"
554
- field :V, :Type => Number, :Version => "1.5"
555
-
556
- def initialize(hash = {})
557
- set_indirect(false)
558
-
559
- super(hash)
560
- end
561
-
562
- end
563
-
564
- class AppData < BuildData
565
- field :REx, :Type => String, :Version => "1.6"
566
- end
567
-
568
- class SigQData < BuildData
569
- field :Preview, :Type => Boolean, :Default => false, :Version => "1.7"
570
- end
571
558
 
572
- end
573
-
574
- module UsageRights
575
-
576
- module Rights
577
-
578
- DOCUMENT_FULLSAVE = [:Document, :FullSave]
579
- DOCUMENT_ALL = DOCUMENT_FULLSAVE
580
-
581
- ANNOTS_CREATE = [:Annots, :Create]
582
- ANNOTS_DELETE = [:Annots, :Delete]
583
- ANNOTS_MODIFY = [:Annots, :Modify]
584
- ANNOTS_COPY = [:Annots, :Copy]
585
- ANNOTS_IMPORT = [:Annots, :Import]
586
- ANNOTS_EXPORT = [:Annots, :Export]
587
- ANNOTS_ONLINE = [:Annots, :Online]
588
- ANNOTS_SUMMARYVIEW = [:Annots, :SummaryView]
589
- ANNOTS_ALL = [ :Annots, :Create, :Modify, :Copy, :Import, :Export, :Online, :SummaryView ]
590
-
591
- FORM_FILLIN = [:Form, :FillIn]
592
- FORM_IMPORT = [:Form, :Import]
593
- FORM_EXPORT = [:Form, :Export]
594
- FORM_SUBMITSTANDALONE = [:Form, :SubmitStandAlone]
595
- FORM_SPAWNTEMPLATE = [:Form, :SpawnTemplate]
596
- FORM_BARCODEPLAINTEXT = [:Form, :BarcodePlaintext]
597
- FORM_ONLINE = [:Form, :Online]
598
- FORM_ALL = [:Form, :FillIn, :Import, :Export, :SubmitStandAlone, :SpawnTemplate, :BarcodePlaintext, :Online]
599
-
600
- FORMEX_BARCODEPLAINTEXT = [:FormEx, :BarcodePlaintext]
601
- FORMEX_ALL = FORMEX_BARCODEPLAINTEXT
602
-
603
- SIGNATURE_MODIFY = [:Signature, :Modify]
604
- SIGNATURE_ALL = SIGNATURE_MODIFY
605
-
606
- EF_CREATE = [:EF, :Create]
607
- EF_DELETE = [:EF, :Delete]
608
- EF_MODIFY = [:EF, :Modify]
609
- EF_IMPORT = [:EF, :Import]
610
- EF_ALL = [:EF, :Create, :Delete, :Modify, :Import]
611
-
612
- ALL = [ DOCUMENT_ALL, ANNOTS_ALL, FORM_ALL, SIGNATURE_ALL, EF_ALL ]
613
-
614
559
  end
615
-
616
- class TransformParams < Dictionary
617
-
618
- include StandardObject
619
-
620
- VERSION = Name.new("2.2")
621
-
622
- field :Type, :Type => Name, :Default => :TransformParams
623
- field :Document, :Type => Array
624
- field :Msg, :Type => String
625
- field :V, :Type => Name, :Default => VERSION
626
- field :Annots, :Type => Array
627
- field :Form, :Type => Array
628
- field :FormEx, :Type => Array
629
- field :Signature, :Type => Array
630
- field :EF, :Type => Array, :Version => "1.6"
631
- field :P, :Type => Boolean, :Default => false, :Version => "1.6"
632
-
633
- def initialize(hash = {})
634
- set_indirect(false)
635
-
636
- super(hash)
637
- end
638
560
 
561
+ module UsageRights
562
+
563
+ module Rights
564
+ DOCUMENT_FULLSAVE = %i[Document FullSave]
565
+ DOCUMENT_ALL = DOCUMENT_FULLSAVE
566
+
567
+ ANNOTS_CREATE = %i[Annots Create]
568
+ ANNOTS_DELETE = %i[Annots Delete]
569
+ ANNOTS_MODIFY = %i[Annots Modify]
570
+ ANNOTS_COPY = %i[Annots Copy]
571
+ ANNOTS_IMPORT = %i[Annots Import]
572
+ ANNOTS_EXPORT = %i[Annots Export]
573
+ ANNOTS_ONLINE = %i[Annots Online]
574
+ ANNOTS_SUMMARYVIEW = %i[Annots SummaryView]
575
+ ANNOTS_ALL = %i[Annots Create Modify Copy Import Export Online SummaryView]
576
+
577
+ FORM_FILLIN = %i[Form FillIn]
578
+ FORM_IMPORT = %i[Form Import]
579
+ FORM_EXPORT = %i[Form Export]
580
+ FORM_SUBMITSTANDALONE = %i[Form SubmitStandAlone]
581
+ FORM_SPAWNTEMPLATE = %i[Form SpawnTemplate]
582
+ FORM_BARCODEPLAINTEXT = %i[Form BarcodePlaintext]
583
+ FORM_ONLINE = %i[Form Online]
584
+ FORM_ALL = %i[Form FillIn Import Export SubmitStandAlone SpawnTemplate BarcodePlaintext Online]
585
+
586
+ FORMEX_BARCODEPLAINTEXT = %i[FormEx BarcodePlaintext]
587
+ FORMEX_ALL = FORMEX_BARCODEPLAINTEXT
588
+
589
+ SIGNATURE_MODIFY = %i[Signature Modify]
590
+ SIGNATURE_ALL = SIGNATURE_MODIFY
591
+
592
+ EF_CREATE = %i[EF Create]
593
+ EF_DELETE = %i[EF Delete]
594
+ EF_MODIFY = %i[EF Modify]
595
+ EF_IMPORT = %i[EF Import]
596
+ EF_ALL = %i[EF Create Delete Modify Import]
597
+
598
+ ALL = [ DOCUMENT_ALL, ANNOTS_ALL, FORM_ALL, SIGNATURE_ALL, EF_ALL ]
599
+ end
600
+
601
+ class TransformParams < Dictionary
602
+ include StandardObject
603
+
604
+ VERSION = Name.new("2.2")
605
+
606
+ field :Type, :Type => Name, :Default => :TransformParams
607
+ field :Document, :Type => Array.of(Name)
608
+ field :Msg, :Type => String
609
+ field :V, :Type => Name, :Default => VERSION
610
+ field :Annots, :Type => Array.of(Name)
611
+ field :Form, :Type => Array.of(Name)
612
+ field :FormEx, :Type => Array.of(Name)
613
+ field :Signature, :Type => Array.of(Name)
614
+ field :EF, :Type => Array.of(Name), :Version => "1.6"
615
+ field :P, :Type => Boolean, :Default => false, :Version => "1.6"
616
+
617
+ def initialize(hash = {}, parser = nil)
618
+ set_indirect(false)
619
+
620
+ super(hash, parser)
621
+ end
622
+ end
639
623
  end
640
-
641
- end
642
624
 
643
625
  end