origami 1.2.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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