origami-docspring 2.2.0 → 2.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/examples/attachments/attachment.rb +7 -8
- data/examples/attachments/nested_document.rb +6 -5
- data/examples/encryption/encryption.rb +5 -4
- data/examples/events/events.rb +7 -6
- data/examples/flash/flash.rb +10 -9
- data/examples/forms/javascript.rb +14 -13
- data/examples/forms/xfa.rb +67 -66
- data/examples/javascript/hello_world.rb +6 -5
- data/examples/javascript/js_emulation.rb +26 -26
- data/examples/loop/goto.rb +12 -11
- data/examples/loop/named.rb +17 -16
- data/examples/signature/signature.rb +11 -11
- data/examples/uri/javascript.rb +25 -24
- data/examples/uri/open-uri.rb +5 -4
- data/examples/uri/submitform.rb +11 -10
- data/lib/origami/3d.rb +330 -334
- data/lib/origami/acroform.rb +267 -268
- data/lib/origami/actions.rb +266 -278
- data/lib/origami/annotations.rb +659 -670
- data/lib/origami/array.rb +192 -196
- data/lib/origami/boolean.rb +66 -70
- data/lib/origami/catalog.rb +360 -363
- data/lib/origami/collections.rb +132 -133
- data/lib/origami/compound.rb +125 -129
- data/lib/origami/destinations.rb +226 -237
- data/lib/origami/dictionary.rb +155 -154
- data/lib/origami/encryption.rb +967 -923
- data/lib/origami/extensions/fdf.rb +270 -275
- data/lib/origami/extensions/ppklite.rb +323 -328
- data/lib/origami/filespec.rb +170 -173
- data/lib/origami/filters/ascii.rb +162 -167
- data/lib/origami/filters/ccitt/tables.rb +248 -252
- data/lib/origami/filters/ccitt.rb +309 -312
- data/lib/origami/filters/crypt.rb +31 -34
- data/lib/origami/filters/dct.rb +47 -50
- data/lib/origami/filters/flate.rb +57 -60
- data/lib/origami/filters/jbig2.rb +50 -53
- data/lib/origami/filters/jpx.rb +40 -43
- data/lib/origami/filters/lzw.rb +151 -155
- data/lib/origami/filters/predictors.rb +250 -255
- data/lib/origami/filters/runlength.rb +111 -115
- data/lib/origami/filters.rb +319 -325
- data/lib/origami/font.rb +173 -177
- data/lib/origami/functions.rb +62 -66
- data/lib/origami/graphics/colors.rb +203 -208
- data/lib/origami/graphics/instruction.rb +79 -81
- data/lib/origami/graphics/path.rb +141 -144
- data/lib/origami/graphics/patterns.rb +156 -160
- data/lib/origami/graphics/render.rb +51 -47
- data/lib/origami/graphics/state.rb +144 -142
- data/lib/origami/graphics/text.rb +185 -188
- data/lib/origami/graphics/xobject.rb +818 -804
- data/lib/origami/graphics.rb +25 -26
- data/lib/origami/header.rb +63 -65
- data/lib/origami/javascript.rb +718 -651
- data/lib/origami/linearization.rb +284 -285
- data/lib/origami/metadata.rb +156 -135
- data/lib/origami/name.rb +98 -100
- data/lib/origami/null.rb +49 -51
- data/lib/origami/numeric.rb +133 -135
- data/lib/origami/obfuscation.rb +180 -182
- data/lib/origami/object.rb +634 -631
- data/lib/origami/optionalcontent.rb +147 -149
- data/lib/origami/outline.rb +46 -48
- data/lib/origami/outputintents.rb +76 -77
- data/lib/origami/page.rb +637 -596
- data/lib/origami/parser.rb +214 -221
- data/lib/origami/parsers/fdf.rb +44 -45
- data/lib/origami/parsers/pdf/lazy.rb +147 -154
- data/lib/origami/parsers/pdf/linear.rb +104 -109
- data/lib/origami/parsers/pdf.rb +109 -107
- data/lib/origami/parsers/ppklite.rb +44 -46
- data/lib/origami/pdf.rb +886 -896
- data/lib/origami/reference.rb +116 -120
- data/lib/origami/signature.rb +617 -625
- data/lib/origami/stream.rb +560 -558
- data/lib/origami/string.rb +366 -368
- data/lib/origami/template/patterns.rb +50 -52
- data/lib/origami/template/widgets.rb +111 -114
- data/lib/origami/trailer.rb +153 -157
- data/lib/origami/tree.rb +55 -57
- data/lib/origami/version.rb +19 -19
- data/lib/origami/webcapture.rb +87 -90
- data/lib/origami/xfa/config.rb +409 -414
- data/lib/origami/xfa/connectionset.rb +113 -117
- data/lib/origami/xfa/datasets.rb +38 -42
- data/lib/origami/xfa/localeset.rb +33 -37
- data/lib/origami/xfa/package.rb +49 -52
- data/lib/origami/xfa/pdf.rb +54 -59
- data/lib/origami/xfa/signature.rb +33 -37
- data/lib/origami/xfa/sourceset.rb +34 -38
- data/lib/origami/xfa/stylesheet.rb +35 -39
- data/lib/origami/xfa/template.rb +1630 -1634
- data/lib/origami/xfa/xdc.rb +33 -37
- data/lib/origami/xfa/xfa.rb +132 -123
- data/lib/origami/xfa/xfdf.rb +34 -38
- data/lib/origami/xfa/xmpmeta.rb +34 -38
- data/lib/origami/xfa.rb +50 -53
- data/lib/origami/xreftable.rb +462 -462
- data/lib/origami.rb +37 -38
- data/test/test_actions.rb +22 -20
- data/test/test_annotations.rb +54 -52
- data/test/test_forms.rb +23 -21
- data/test/test_native_types.rb +82 -78
- data/test/test_object_tree.rb +25 -24
- data/test/test_pages.rb +43 -41
- data/test/test_pdf.rb +2 -0
- data/test/test_pdf_attachment.rb +23 -21
- data/test/test_pdf_create.rb +16 -15
- data/test/test_pdf_encrypt.rb +69 -66
- data/test/test_pdf_parse.rb +131 -129
- data/test/test_pdf_parse_lazy.rb +53 -53
- data/test/test_pdf_sign.rb +67 -67
- data/test/test_streams.rb +145 -143
- data/test/test_xrefs.rb +46 -45
- metadata +64 -8
data/lib/origami/signature.rb
CHANGED
@@ -1,702 +1,694 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# This file is part of Origami, PDF manipulation framework for Ruby
|
5
|
+
# Copyright (C) 2016 Guillaume Delugré.
|
6
|
+
#
|
7
|
+
# Origami is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU Lesser General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# Origami is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public License
|
18
|
+
# along with Origami. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
#
|
2
20
|
|
3
|
-
|
4
|
-
|
21
|
+
require 'openssl'
|
22
|
+
require 'digest/sha1'
|
5
23
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
24
|
+
module Origami
|
25
|
+
class SignatureError < Error # :nodoc:
|
26
|
+
end
|
27
|
+
|
28
|
+
class PDF
|
29
|
+
#
|
30
|
+
# Verify a document signature.
|
31
|
+
# _:trusted_certs_: an array of trusted X509 certificates.
|
32
|
+
# _:use_system_store_: use the system store for certificate authorities.
|
33
|
+
# _:allow_self_signed_: allow self-signed certificates in the verification chain.
|
34
|
+
# _verify_cb_: block called when encountering a certificate that cannot be verified.
|
35
|
+
# Passed argument in the OpenSSL::X509::StoreContext.
|
36
|
+
#
|
37
|
+
def verify(trusted_certs: [],
|
38
|
+
use_system_store: false,
|
39
|
+
allow_self_signed: false,
|
40
|
+
&verify_cb)
|
41
|
+
digsig = signature
|
42
|
+
digsig = digsig.cast_to(Signature::DigitalSignature) unless digsig.is_a?(Signature::DigitalSignature)
|
43
|
+
|
44
|
+
signature = digsig.signature_data
|
45
|
+
chain = digsig.certificate_chain
|
46
|
+
subfilter = digsig.SubFilter.value
|
47
|
+
|
48
|
+
store = OpenSSL::X509::Store.new
|
49
|
+
store.set_default_paths if use_system_store
|
50
|
+
trusted_certs.each { |ca| store.add_cert(ca) }
|
51
|
+
|
52
|
+
store.verify_callback = ->(success, ctx) {
|
53
|
+
return true if success
|
54
|
+
|
55
|
+
error = ctx.error
|
56
|
+
is_self_signed = error == OpenSSL::X509::V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
|
57
|
+
error == OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN
|
58
|
+
|
59
|
+
return true if is_self_signed && allow_self_signed && verify_cb.nil?
|
60
|
+
|
61
|
+
verify_cb.call(ctx) unless verify_cb.nil?
|
62
|
+
}
|
63
|
+
|
64
|
+
data = extract_signed_data(digsig)
|
65
|
+
Signature.verify(subfilter.to_s, data, signature, store, chain)
|
66
|
+
end
|
10
67
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
68
|
+
#
|
69
|
+
# Sign the document with the given key and x509 certificate.
|
70
|
+
# _certificate_:: The X509 certificate containing the public key.
|
71
|
+
# _key_:: The private key associated with the certificate.
|
72
|
+
# _method_:: The PDF signature identifier.
|
73
|
+
# _ca_:: Optional CA certificates used to sign the user certificate.
|
74
|
+
# _annotation_:: Annotation associated with the signature.
|
75
|
+
# _issuer_:: Issuer name.
|
76
|
+
# _location_:: Signature location.
|
77
|
+
# _contact_:: Signer contact.
|
78
|
+
# _reason_:: Signing reason.
|
79
|
+
#
|
80
|
+
def sign(certificate, key,
|
81
|
+
method: Signature::PKCS7_DETACHED,
|
82
|
+
ca: [],
|
83
|
+
annotation: nil,
|
84
|
+
issuer: nil,
|
85
|
+
location: nil,
|
86
|
+
contact: nil,
|
87
|
+
reason: nil)
|
88
|
+
unless certificate.is_a?(OpenSSL::X509::Certificate)
|
89
|
+
raise TypeError, "A OpenSSL::X509::Certificate object must be passed."
|
90
|
+
end
|
91
|
+
|
92
|
+
unless key.is_a?(OpenSSL::PKey::RSA)
|
93
|
+
raise TypeError, "A OpenSSL::PKey::RSA object must be passed."
|
94
|
+
end
|
95
|
+
|
96
|
+
unless ca.is_a?(::Array)
|
97
|
+
raise TypeError, "Expected an Array of CA certificates."
|
98
|
+
end
|
99
|
+
|
100
|
+
unless annotation.nil? || annotation.is_a?(Annotation::Widget::Signature)
|
101
|
+
raise TypeError, "Expected a Annotation::Widget::Signature object."
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# XXX: Currently signing a linearized document will result in a broken document.
|
106
|
+
# Delinearize the document first until we find a proper way to handle this case.
|
107
|
+
#
|
108
|
+
if linearized?
|
109
|
+
delinearize!
|
110
|
+
end
|
111
|
+
|
112
|
+
digsig = Signature::DigitalSignature.new.set_indirect(true)
|
113
|
+
|
114
|
+
if annotation.nil?
|
115
|
+
annotation = Annotation::Widget::Signature.new
|
116
|
+
annotation.Rect = Rectangle[llx: 0.0, lly: 0.0, urx: 0.0, ury: 0.0]
|
117
|
+
end
|
118
|
+
|
119
|
+
annotation.V = digsig
|
120
|
+
add_fields(annotation)
|
121
|
+
self.Catalog.AcroForm.SigFlags =
|
122
|
+
InteractiveForm::SigFlags::SIGNATURES_EXIST | InteractiveForm::SigFlags::APPEND_ONLY
|
123
|
+
|
124
|
+
digsig.Type = :Sig
|
125
|
+
digsig.Contents = HexaString.new("\x00" * Signature.required_size(method, certificate, key, ca))
|
126
|
+
digsig.Filter = :'Adobe.PPKLite'
|
127
|
+
digsig.SubFilter = Name.new(method)
|
128
|
+
digsig.ByteRange = [0, 0, 0, 0]
|
129
|
+
digsig.Name = issuer
|
130
|
+
|
131
|
+
digsig.Location = HexaString.new(location) if location
|
132
|
+
digsig.ContactInfo = HexaString.new(contact) if contact
|
133
|
+
digsig.Reason = HexaString.new(reason) if reason
|
134
|
+
|
135
|
+
# PKCS1 signatures require a Cert entry.
|
136
|
+
if method == Signature::PKCS1_RSA_SHA1
|
137
|
+
digsig.Cert =
|
138
|
+
if ca.empty?
|
139
|
+
HexaString.new(certificate.to_der)
|
140
|
+
else
|
141
|
+
[HexaString.new(certificate.to_der)] + ca.map { |crt| HexaString.new(crt.to_der) }
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Flattening the PDF to get file view.
|
147
|
+
#
|
148
|
+
compile
|
149
|
+
|
150
|
+
#
|
151
|
+
# Creating an empty Xref table to compute signature byte range.
|
152
|
+
#
|
153
|
+
rebuild_dummy_xrefs
|
154
|
+
|
155
|
+
sig_offset = get_object_offset(digsig.no, digsig.generation) + digsig.signature_offset
|
156
|
+
|
157
|
+
digsig.ByteRange[0] = 0
|
158
|
+
digsig.ByteRange[1] = sig_offset
|
159
|
+
digsig.ByteRange[2] = sig_offset + digsig.Contents.to_s.bytesize
|
160
|
+
|
161
|
+
until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
|
162
|
+
digsig.ByteRange[3] = filesize - digsig.ByteRange[2]
|
163
|
+
end
|
164
|
+
|
165
|
+
# From that point on, the file size remains constant
|
166
|
+
|
167
|
+
#
|
168
|
+
# Correct Xrefs variations caused by ByteRange modifications.
|
169
|
+
#
|
170
|
+
rebuild_xrefs
|
171
|
+
|
172
|
+
file_data = output
|
173
|
+
signable_data = file_data[digsig.ByteRange[0], digsig.ByteRange[1]] +
|
174
|
+
file_data[digsig.ByteRange[2], digsig.ByteRange[3]]
|
175
|
+
|
176
|
+
#
|
177
|
+
# Computes and inserts the signature.
|
178
|
+
#
|
179
|
+
signature = Signature.compute(method, signable_data, certificate, key, ca)
|
180
|
+
digsig.Contents[0, signature.size] = signature
|
181
|
+
|
182
|
+
#
|
183
|
+
# No more modification are allowed after signing.
|
184
|
+
#
|
185
|
+
freeze
|
186
|
+
end
|
15
187
|
|
16
|
-
|
17
|
-
|
188
|
+
#
|
189
|
+
# Returns whether the document contains a digital signature.
|
190
|
+
#
|
191
|
+
def signed?
|
192
|
+
self.Catalog.AcroForm.is_a?(Dictionary) and
|
193
|
+
self.Catalog.AcroForm.SigFlags.is_a?(Integer) and
|
194
|
+
(self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::SIGNATURES_EXIST != 0)
|
195
|
+
rescue InvalidReferenceError
|
196
|
+
false
|
197
|
+
end
|
18
198
|
|
19
|
-
|
199
|
+
#
|
200
|
+
# Enable the document Usage Rights.
|
201
|
+
# _rights_:: list of rights defined in UsageRights::Rights
|
202
|
+
#
|
203
|
+
def enable_usage_rights(cert, pkey, *rights)
|
204
|
+
# Always uses a detached PKCS7 signature for UR.
|
205
|
+
method = Signature::PKCS7_DETACHED
|
206
|
+
|
207
|
+
#
|
208
|
+
# Load key pair
|
209
|
+
#
|
210
|
+
key = pkey.is_a?(OpenSSL::PKey::RSA) ? pkey : OpenSSL::PKey::RSA.new(pkey)
|
211
|
+
certificate = cert.is_a?(OpenSSL::X509::Certificate) ? cert : OpenSSL::X509::Certificate.new(cert)
|
212
|
+
|
213
|
+
#
|
214
|
+
# Forge digital signature dictionary
|
215
|
+
#
|
216
|
+
digsig = Signature::DigitalSignature.new.set_indirect(true)
|
217
|
+
|
218
|
+
self.Catalog.AcroForm ||= InteractiveForm.new
|
219
|
+
# self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::APPEND_ONLY
|
220
|
+
|
221
|
+
digsig.Type = :Sig
|
222
|
+
digsig.Contents = HexaString.new("\x00" * Signature.required_size(method, certificate, key, []))
|
223
|
+
digsig.Filter = :'Adobe.PPKLite'
|
224
|
+
digsig.Name = "ARE Acrobat Product v8.0 P23 0002337"
|
225
|
+
digsig.SubFilter = Name.new(method)
|
226
|
+
digsig.ByteRange = [0, 0, 0, 0]
|
227
|
+
|
228
|
+
sigref = Signature::Reference.new
|
229
|
+
sigref.Type = :SigRef
|
230
|
+
sigref.TransformMethod = :UR3
|
231
|
+
sigref.Data = self.Catalog
|
232
|
+
|
233
|
+
sigref.TransformParams = UsageRights::TransformParams.new
|
234
|
+
sigref.TransformParams.P = true
|
235
|
+
sigref.TransformParams.Type = :TransformParams
|
236
|
+
sigref.TransformParams.V = UsageRights::TransformParams::VERSION
|
237
|
+
|
238
|
+
rights.each do |right|
|
239
|
+
sigref.TransformParams[right.first] ||= []
|
240
|
+
sigref.TransformParams[right.first].concat(right[1..])
|
241
|
+
end
|
242
|
+
|
243
|
+
digsig.Reference = [sigref]
|
244
|
+
|
245
|
+
self.Catalog.Perms ||= Perms.new
|
246
|
+
self.Catalog.Perms.UR3 = digsig
|
247
|
+
|
248
|
+
#
|
249
|
+
# Flattening the PDF to get file view.
|
250
|
+
#
|
251
|
+
compile
|
252
|
+
|
253
|
+
#
|
254
|
+
# Creating an empty Xref table to compute signature byte range.
|
255
|
+
#
|
256
|
+
rebuild_dummy_xrefs
|
257
|
+
|
258
|
+
sig_offset = get_object_offset(digsig.no, digsig.generation) + digsig.signature_offset
|
259
|
+
|
260
|
+
digsig.ByteRange[0] = 0
|
261
|
+
digsig.ByteRange[1] = sig_offset
|
262
|
+
digsig.ByteRange[2] = sig_offset + digsig.Contents.size
|
263
|
+
|
264
|
+
until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
|
265
|
+
digsig.ByteRange[3] = filesize - digsig.ByteRange[2]
|
266
|
+
end
|
267
|
+
|
268
|
+
# From that point on, the file size remains constant
|
269
|
+
|
270
|
+
#
|
271
|
+
# Correct Xrefs variations caused by ByteRange modifications.
|
272
|
+
#
|
273
|
+
rebuild_xrefs
|
274
|
+
|
275
|
+
file_data = output
|
276
|
+
signable_data = file_data[digsig.ByteRange[0], digsig.ByteRange[1]] +
|
277
|
+
file_data[digsig.ByteRange[2], digsig.ByteRange[3]]
|
278
|
+
|
279
|
+
signature = Signature.compute(method, signable_data, certificate, key, [])
|
280
|
+
digsig.Contents[0, signature.size] = signature
|
281
|
+
|
282
|
+
#
|
283
|
+
# No more modification are allowed after signing.
|
284
|
+
#
|
285
|
+
freeze
|
286
|
+
end
|
20
287
|
|
21
|
-
|
22
|
-
|
288
|
+
def usage_rights?
|
289
|
+
!self.Catalog.Perms.nil? and
|
290
|
+
(!self.Catalog.Perms.has_key?(:UR3) or !self.Catalog.Perms.has_key?(:UR))
|
291
|
+
end
|
23
292
|
|
24
|
-
|
293
|
+
def signature
|
294
|
+
raise SignatureError, "Not a signed document" unless signed?
|
295
|
+
|
296
|
+
each_field do |field|
|
297
|
+
return field.V if (field.FT == :Sig) && field.V.is_a?(Dictionary)
|
298
|
+
end
|
25
299
|
|
26
|
-
|
300
|
+
raise SignatureError, "Cannot find digital signature"
|
27
301
|
end
|
28
302
|
|
29
|
-
|
30
|
-
#
|
31
|
-
# Verify a document signature.
|
32
|
-
# _:trusted_certs_: an array of trusted X509 certificates.
|
33
|
-
# _:use_system_store_: use the system store for certificate authorities.
|
34
|
-
# _:allow_self_signed_: allow self-signed certificates in the verification chain.
|
35
|
-
# _verify_cb_: block called when encountering a certificate that cannot be verified.
|
36
|
-
# Passed argument in the OpenSSL::X509::StoreContext.
|
37
|
-
#
|
38
|
-
def verify(trusted_certs: [],
|
39
|
-
use_system_store: false,
|
40
|
-
allow_self_signed: false,
|
41
|
-
&verify_cb)
|
303
|
+
private
|
42
304
|
|
43
|
-
|
44
|
-
|
305
|
+
#
|
306
|
+
# Verifies the ByteRange field of a digital signature and returned the signed data.
|
307
|
+
#
|
308
|
+
def extract_signed_data(digsig)
|
309
|
+
# Computes the boundaries of the Contents field.
|
310
|
+
start_sig = digsig[:Contents].file_offset
|
45
311
|
|
46
|
-
|
47
|
-
|
48
|
-
|
312
|
+
stream = StringScanner.new(original_data)
|
313
|
+
stream.pos = digsig[:Contents].file_offset
|
314
|
+
Object.typeof(stream).parse(stream)
|
315
|
+
end_sig = stream.pos
|
316
|
+
stream.terminate
|
49
317
|
|
50
|
-
|
51
|
-
|
52
|
-
|
318
|
+
r1, r2 = digsig.ranges
|
319
|
+
if (r1.begin != 0) ||
|
320
|
+
(r2.end != original_data.size) ||
|
321
|
+
(r1.end != start_sig) ||
|
322
|
+
(r2.begin != end_sig)
|
53
323
|
|
54
|
-
|
55
|
-
|
324
|
+
raise SignatureError, "Invalid signature byte range"
|
325
|
+
end
|
56
326
|
|
57
|
-
|
58
|
-
|
59
|
-
|
327
|
+
original_data[r1] + original_data[r2]
|
328
|
+
end
|
329
|
+
end
|
60
330
|
|
61
|
-
|
331
|
+
class Perms < Dictionary
|
332
|
+
include StandardObject
|
62
333
|
|
63
|
-
|
64
|
-
|
334
|
+
field :DocMDP, Type: Dictionary
|
335
|
+
field :UR, Type: Dictionary
|
336
|
+
field :UR3, Type: Dictionary, Version: "1.6"
|
337
|
+
end
|
65
338
|
|
66
|
-
|
67
|
-
|
68
|
-
|
339
|
+
module Signature
|
340
|
+
PKCS1_RSA_SHA1 = "adbe.x509.rsa_sha1"
|
341
|
+
PKCS7_SHA1 = "adbe.pkcs7.sha1"
|
342
|
+
PKCS7_DETACHED = "adbe.pkcs7.detached"
|
69
343
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
# _ca_:: Optional CA certificates used to sign the user certificate.
|
76
|
-
# _annotation_:: Annotation associated with the signature.
|
77
|
-
# _issuer_:: Issuer name.
|
78
|
-
# _location_:: Signature location.
|
79
|
-
# _contact_:: Signer contact.
|
80
|
-
# _reason_:: Signing reason.
|
81
|
-
#
|
82
|
-
def sign(certificate, key,
|
83
|
-
method: Signature::PKCS7_DETACHED,
|
84
|
-
ca: [],
|
85
|
-
annotation: nil,
|
86
|
-
issuer: nil,
|
87
|
-
location: nil,
|
88
|
-
contact: nil,
|
89
|
-
reason: nil)
|
90
|
-
|
91
|
-
unless certificate.is_a?(OpenSSL::X509::Certificate)
|
92
|
-
raise TypeError, "A OpenSSL::X509::Certificate object must be passed."
|
93
|
-
end
|
94
|
-
|
95
|
-
unless key.is_a?(OpenSSL::PKey::RSA)
|
96
|
-
raise TypeError, "A OpenSSL::PKey::RSA object must be passed."
|
97
|
-
end
|
98
|
-
|
99
|
-
unless ca.is_a?(::Array)
|
100
|
-
raise TypeError, "Expected an Array of CA certificates."
|
101
|
-
end
|
102
|
-
|
103
|
-
unless annotation.nil? or annotation.is_a?(Annotation::Widget::Signature)
|
104
|
-
raise TypeError, "Expected a Annotation::Widget::Signature object."
|
105
|
-
end
|
106
|
-
|
107
|
-
#
|
108
|
-
# XXX: Currently signing a linearized document will result in a broken document.
|
109
|
-
# Delinearize the document first until we find a proper way to handle this case.
|
110
|
-
#
|
111
|
-
if self.linearized?
|
112
|
-
self.delinearize!
|
113
|
-
end
|
114
|
-
|
115
|
-
digsig = Signature::DigitalSignature.new.set_indirect(true)
|
116
|
-
|
117
|
-
if annotation.nil?
|
118
|
-
annotation = Annotation::Widget::Signature.new
|
119
|
-
annotation.Rect = Rectangle[:llx => 0.0, :lly => 0.0, :urx => 0.0, :ury => 0.0]
|
120
|
-
end
|
121
|
-
|
122
|
-
annotation.V = digsig
|
123
|
-
add_fields(annotation)
|
124
|
-
self.Catalog.AcroForm.SigFlags =
|
125
|
-
InteractiveForm::SigFlags::SIGNATURES_EXIST | InteractiveForm::SigFlags::APPEND_ONLY
|
126
|
-
|
127
|
-
digsig.Type = :Sig
|
128
|
-
digsig.Contents = HexaString.new("\x00" * Signature::required_size(method, certificate, key, ca))
|
129
|
-
digsig.Filter = :"Adobe.PPKLite"
|
130
|
-
digsig.SubFilter = Name.new(method)
|
131
|
-
digsig.ByteRange = [0, 0, 0, 0]
|
132
|
-
digsig.Name = issuer
|
133
|
-
|
134
|
-
digsig.Location = HexaString.new(location) if location
|
135
|
-
digsig.ContactInfo = HexaString.new(contact) if contact
|
136
|
-
digsig.Reason = HexaString.new(reason) if reason
|
137
|
-
|
138
|
-
# PKCS1 signatures require a Cert entry.
|
139
|
-
if method == Signature::PKCS1_RSA_SHA1
|
140
|
-
digsig.Cert =
|
141
|
-
if ca.empty?
|
142
|
-
HexaString.new(certificate.to_der)
|
143
|
-
else
|
144
|
-
[ HexaString.new(certificate.to_der) ] + ca.map{ |crt| HexaString.new(crt.to_der) }
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
#
|
149
|
-
# Flattening the PDF to get file view.
|
150
|
-
#
|
151
|
-
compile
|
152
|
-
|
153
|
-
#
|
154
|
-
# Creating an empty Xref table to compute signature byte range.
|
155
|
-
#
|
156
|
-
rebuild_dummy_xrefs
|
157
|
-
|
158
|
-
sig_offset = get_object_offset(digsig.no, digsig.generation) + digsig.signature_offset
|
159
|
-
|
160
|
-
digsig.ByteRange[0] = 0
|
161
|
-
digsig.ByteRange[1] = sig_offset
|
162
|
-
digsig.ByteRange[2] = sig_offset + digsig.Contents.to_s.bytesize
|
163
|
-
|
164
|
-
until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
|
165
|
-
digsig.ByteRange[3] = filesize - digsig.ByteRange[2]
|
166
|
-
end
|
167
|
-
|
168
|
-
# From that point on, the file size remains constant
|
169
|
-
|
170
|
-
#
|
171
|
-
# Correct Xrefs variations caused by ByteRange modifications.
|
172
|
-
#
|
173
|
-
rebuild_xrefs
|
174
|
-
|
175
|
-
file_data = output()
|
176
|
-
signable_data = file_data[digsig.ByteRange[0],digsig.ByteRange[1]] +
|
177
|
-
file_data[digsig.ByteRange[2],digsig.ByteRange[3]]
|
178
|
-
|
179
|
-
#
|
180
|
-
# Computes and inserts the signature.
|
181
|
-
#
|
182
|
-
signature = Signature.compute(method, signable_data, certificate, key, ca)
|
183
|
-
digsig.Contents[0, signature.size] = signature
|
184
|
-
|
185
|
-
#
|
186
|
-
# No more modification are allowed after signing.
|
187
|
-
#
|
188
|
-
self.freeze
|
189
|
-
end
|
344
|
+
#
|
345
|
+
# PKCS1 class used for adbe.x509.rsa_sha1.
|
346
|
+
#
|
347
|
+
class PKCS1
|
348
|
+
class PKCS1Error < SignatureError; end
|
190
349
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
def signed?
|
195
|
-
begin
|
196
|
-
self.Catalog.AcroForm.is_a?(Dictionary) and
|
197
|
-
self.Catalog.AcroForm.SigFlags.is_a?(Integer) and
|
198
|
-
(self.Catalog.AcroForm.SigFlags & InteractiveForm::SigFlags::SIGNATURES_EXIST != 0)
|
199
|
-
rescue InvalidReferenceError
|
200
|
-
false
|
201
|
-
end
|
202
|
-
end
|
350
|
+
def initialize(signature)
|
351
|
+
@signature_object = decode_pkcs1(signature)
|
352
|
+
end
|
203
353
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
#
|
208
|
-
def enable_usage_rights(cert, pkey, *rights)
|
209
|
-
|
210
|
-
# Always uses a detached PKCS7 signature for UR.
|
211
|
-
method = Signature::PKCS7_DETACHED
|
212
|
-
|
213
|
-
#
|
214
|
-
# Load key pair
|
215
|
-
#
|
216
|
-
key = pkey.is_a?(OpenSSL::PKey::RSA) ? pkey : OpenSSL::PKey::RSA.new(pkey)
|
217
|
-
certificate = cert.is_a?(OpenSSL::X509::Certificate) ? cert : OpenSSL::X509::Certificate.new(cert)
|
218
|
-
|
219
|
-
#
|
220
|
-
# Forge digital signature dictionary
|
221
|
-
#
|
222
|
-
digsig = Signature::DigitalSignature.new.set_indirect(true)
|
223
|
-
|
224
|
-
self.Catalog.AcroForm ||= InteractiveForm.new
|
225
|
-
#self.Catalog.AcroForm.SigFlags = InteractiveForm::SigFlags::APPEND_ONLY
|
226
|
-
|
227
|
-
digsig.Type = :Sig
|
228
|
-
digsig.Contents = HexaString.new("\x00" * Signature.required_size(method, certificate, key, []))
|
229
|
-
digsig.Filter = :"Adobe.PPKLite"
|
230
|
-
digsig.Name = "ARE Acrobat Product v8.0 P23 0002337"
|
231
|
-
digsig.SubFilter = Name.new(method )
|
232
|
-
digsig.ByteRange = [0, 0, 0, 0]
|
233
|
-
|
234
|
-
sigref = Signature::Reference.new
|
235
|
-
sigref.Type = :SigRef
|
236
|
-
sigref.TransformMethod = :UR3
|
237
|
-
sigref.Data = self.Catalog
|
238
|
-
|
239
|
-
sigref.TransformParams = UsageRights::TransformParams.new
|
240
|
-
sigref.TransformParams.P = true
|
241
|
-
sigref.TransformParams.Type = :TransformParams
|
242
|
-
sigref.TransformParams.V = UsageRights::TransformParams::VERSION
|
243
|
-
|
244
|
-
rights.each do |right|
|
245
|
-
sigref.TransformParams[right.first] ||= []
|
246
|
-
sigref.TransformParams[right.first].concat(right[1..-1])
|
247
|
-
end
|
248
|
-
|
249
|
-
digsig.Reference = [ sigref ]
|
250
|
-
|
251
|
-
self.Catalog.Perms ||= Perms.new
|
252
|
-
self.Catalog.Perms.UR3 = digsig
|
253
|
-
|
254
|
-
#
|
255
|
-
# Flattening the PDF to get file view.
|
256
|
-
#
|
257
|
-
compile
|
258
|
-
|
259
|
-
#
|
260
|
-
# Creating an empty Xref table to compute signature byte range.
|
261
|
-
#
|
262
|
-
rebuild_dummy_xrefs
|
263
|
-
|
264
|
-
sig_offset = get_object_offset(digsig.no, digsig.generation) + digsig.signature_offset
|
265
|
-
|
266
|
-
digsig.ByteRange[0] = 0
|
267
|
-
digsig.ByteRange[1] = sig_offset
|
268
|
-
digsig.ByteRange[2] = sig_offset + digsig.Contents.size
|
269
|
-
|
270
|
-
until digsig.ByteRange[3] == filesize - digsig.ByteRange[2]
|
271
|
-
digsig.ByteRange[3] = filesize - digsig.ByteRange[2]
|
272
|
-
end
|
273
|
-
|
274
|
-
# From that point on, the file size remains constant
|
275
|
-
|
276
|
-
#
|
277
|
-
# Correct Xrefs variations caused by ByteRange modifications.
|
278
|
-
#
|
279
|
-
rebuild_xrefs
|
280
|
-
|
281
|
-
file_data = output()
|
282
|
-
signable_data = file_data[digsig.ByteRange[0],digsig.ByteRange[1]] +
|
283
|
-
file_data[digsig.ByteRange[2],digsig.ByteRange[3]]
|
284
|
-
|
285
|
-
signature = Signature.compute(method, signable_data, certificate, key, [])
|
286
|
-
digsig.Contents[0, signature.size] = signature
|
287
|
-
|
288
|
-
#
|
289
|
-
# No more modification are allowed after signing.
|
290
|
-
#
|
291
|
-
self.freeze
|
292
|
-
end
|
293
|
-
|
294
|
-
def usage_rights?
|
295
|
-
not self.Catalog.Perms.nil? and
|
296
|
-
(not self.Catalog.Perms.has_key?(:UR3) or not self.Catalog.Perms.has_key?(:UR))
|
297
|
-
end
|
354
|
+
def verify(certificate, chain, store, data)
|
355
|
+
store.verify(certificate, chain) and certificate.public_key.verify(OpenSSL::Digest.new('SHA1'), @signature_object.value, data)
|
356
|
+
end
|
298
357
|
|
299
|
-
|
300
|
-
|
358
|
+
def self.sign(certificate, key, data)
|
359
|
+
raise PKCS1Error, "Invalid key for certificate" unless certificate.check_private_key(key)
|
301
360
|
|
302
|
-
|
303
|
-
|
304
|
-
end
|
361
|
+
new encode_pkcs1 key.sign(OpenSSL::Digest.new('SHA1'), data)
|
362
|
+
end
|
305
363
|
|
306
|
-
|
307
|
-
|
364
|
+
def to_der
|
365
|
+
@signature_object.to_der
|
366
|
+
end
|
308
367
|
|
309
|
-
|
368
|
+
private
|
310
369
|
|
370
|
+
def decode_pkcs1(data)
|
311
371
|
#
|
312
|
-
#
|
372
|
+
# Extracts the first ASN.1 object from the data and discards the rest.
|
373
|
+
# Must be an octet string.
|
313
374
|
#
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
stream = StringScanner.new(self.original_data)
|
319
|
-
stream.pos = digsig[:Contents].file_offset
|
320
|
-
Object.typeof(stream).parse(stream)
|
321
|
-
end_sig = stream.pos
|
322
|
-
stream.terminate
|
323
|
-
|
324
|
-
r1, r2 = digsig.ranges
|
325
|
-
if r1.begin != 0 or
|
326
|
-
r2.end != self.original_data.size or
|
327
|
-
r1.end != start_sig or
|
328
|
-
r2.begin != end_sig
|
329
|
-
|
330
|
-
raise SignatureError, "Invalid signature byte range"
|
331
|
-
end
|
332
|
-
|
333
|
-
self.original_data[r1] + self.original_data[r2]
|
334
|
-
end
|
375
|
+
signature_len = 0
|
376
|
+
OpenSSL::ASN1.traverse(data) do |_, offset, hdr_len, len, _, _, tag|
|
377
|
+
raise PKCS1Error, "Invalid PKCS1 object, expected an ASN.1 octet string" unless tag == OpenSSL::ASN1::OCTET_STRING
|
335
378
|
|
336
|
-
|
379
|
+
signature_len = offset + hdr_len + len
|
380
|
+
break
|
381
|
+
end
|
337
382
|
|
338
|
-
|
339
|
-
|
383
|
+
OpenSSL::ASN1.decode(data[0, signature_len])
|
384
|
+
end
|
340
385
|
|
341
|
-
|
342
|
-
|
343
|
-
|
386
|
+
def self.encode_pkcs1(data)
|
387
|
+
OpenSSL::ASN1::OctetString.new(data).to_der
|
388
|
+
end
|
389
|
+
private_class_method :encode_pkcs1
|
344
390
|
end
|
345
391
|
|
346
|
-
|
392
|
+
def self.verify(method, data, signature, store, chain)
|
393
|
+
case method
|
394
|
+
when PKCS7_DETACHED
|
395
|
+
pkcs7 = OpenSSL::PKCS7.new(signature)
|
396
|
+
raise SignatureError, "Not a PKCS7 detached signature" unless pkcs7.detached?
|
397
|
+
pkcs7.verify([], store, data, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY)
|
398
|
+
|
399
|
+
when PKCS7_SHA1
|
400
|
+
pkcs7 = OpenSSL::PKCS7.new(signature)
|
401
|
+
pkcs7.verify([], store, nil, OpenSSL::PKCS7::BINARY) and pkcs7.data == Digest::SHA1.digest(data)
|
402
|
+
|
403
|
+
when PKCS1_RSA_SHA1
|
404
|
+
raise SignatureError, "Cannot verify RSA signature without a certificate" if chain.empty?
|
405
|
+
cert = chain.shift
|
406
|
+
pkcs1 = PKCS1.new(signature)
|
407
|
+
pkcs1.verify(cert, chain, store, data)
|
408
|
+
|
409
|
+
else
|
410
|
+
raise NotImplementedError, "Unsupported signature method #{method.inspect}"
|
411
|
+
end
|
412
|
+
end
|
347
413
|
|
348
|
-
|
349
|
-
|
350
|
-
|
414
|
+
#
|
415
|
+
# Computes the required size in bytes for storing the signature.
|
416
|
+
#
|
417
|
+
def self.required_size(method, certificate, key, ca)
|
418
|
+
compute(method, "", certificate, key, ca).size
|
419
|
+
end
|
351
420
|
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
421
|
+
#
|
422
|
+
# Computes the signature using the specified subfilter method.
|
423
|
+
#
|
424
|
+
def self.compute(method, data, certificate, key, ca)
|
425
|
+
case method
|
426
|
+
when PKCS7_DETACHED
|
427
|
+
OpenSSL::PKCS7.sign(certificate, key, data, ca, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
|
357
428
|
|
358
|
-
|
359
|
-
|
360
|
-
end
|
429
|
+
when PKCS7_SHA1
|
430
|
+
OpenSSL::PKCS7.sign(certificate, key, Digest::SHA1.digest(data), ca, OpenSSL::PKCS7::BINARY).to_der
|
361
431
|
|
362
|
-
|
363
|
-
|
364
|
-
end
|
432
|
+
when PKCS1_RSA_SHA1
|
433
|
+
PKCS1.sign(certificate, key, data).to_der
|
365
434
|
|
366
|
-
|
367
|
-
|
435
|
+
else
|
436
|
+
raise NotImplementedError, "Unsupported signature method #{method.inspect}"
|
437
|
+
end
|
438
|
+
end
|
368
439
|
|
369
|
-
|
370
|
-
|
440
|
+
#
|
441
|
+
# Class representing a signature which can be embedded in DigitalSignature dictionary.
|
442
|
+
# It must be a direct object.
|
443
|
+
#
|
444
|
+
class Reference < Dictionary
|
445
|
+
include StandardObject
|
371
446
|
|
372
|
-
|
373
|
-
@signature_object.to_der
|
374
|
-
end
|
447
|
+
add_type_signature Type: :SigRef
|
375
448
|
|
376
|
-
|
449
|
+
field :Type, Type: Name, Default: :SigRef
|
450
|
+
field :TransformMethod, Type: Name, Default: :DocMDP, Required: true
|
451
|
+
field :TransformParams, Type: Dictionary
|
452
|
+
field :Data, Type: Object
|
453
|
+
field :DigestMethod, Type: Name, Default: :MD5
|
454
|
+
field :DigestValue, Type: String
|
455
|
+
field :DigestLocation, Type: Array
|
377
456
|
|
378
|
-
|
379
|
-
|
380
|
-
# Extracts the first ASN.1 object from the data and discards the rest.
|
381
|
-
# Must be an octet string.
|
382
|
-
#
|
383
|
-
signature_len = 0
|
384
|
-
OpenSSL::ASN1.traverse(data) do |_, offset, hdr_len, len, _, _, tag|
|
385
|
-
raise PKCS1Error, "Invalid PKCS1 object, expected an ASN.1 octet string" unless tag == OpenSSL::ASN1::OCTET_STRING
|
457
|
+
def initialize(hash = {}, parser = nil)
|
458
|
+
set_indirect(false)
|
386
459
|
|
387
|
-
|
388
|
-
|
389
|
-
|
460
|
+
super
|
461
|
+
end
|
462
|
+
end
|
390
463
|
|
391
|
-
|
392
|
-
|
464
|
+
class BuildData < Dictionary
|
465
|
+
include StandardObject
|
393
466
|
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
467
|
+
field :Name, Type: Name, Version: "1.5"
|
468
|
+
field :Date, Type: String, Version: "1.5"
|
469
|
+
field :R, Type: Number, Version: "1.5"
|
470
|
+
field :PreRelease, Type: Boolean, Default: false, Version: "1.5"
|
471
|
+
field :OS, Type: Array, Version: "1.5"
|
472
|
+
field :NonEFontNoWarn, Type: Boolean, Version: "1.5"
|
473
|
+
field :TrustedMode, Type: Boolean, Version: "1.5"
|
474
|
+
field :V, Type: Number, Version: "1.5"
|
399
475
|
|
400
|
-
|
401
|
-
|
402
|
-
when PKCS7_DETACHED
|
403
|
-
pkcs7 = OpenSSL::PKCS7.new(signature)
|
404
|
-
raise SignatureError, "Not a PKCS7 detached signature" unless pkcs7.detached?
|
405
|
-
pkcs7.verify([], store, data, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY)
|
406
|
-
|
407
|
-
when PKCS7_SHA1
|
408
|
-
pkcs7 = OpenSSL::PKCS7.new(signature)
|
409
|
-
pkcs7.verify([], store, nil, OpenSSL::PKCS7::BINARY) and pkcs7.data == Digest::SHA1.digest(data)
|
410
|
-
|
411
|
-
when PKCS1_RSA_SHA1
|
412
|
-
raise SignatureError, "Cannot verify RSA signature without a certificate" if chain.empty?
|
413
|
-
cert = chain.shift
|
414
|
-
pkcs1 = PKCS1.new(signature)
|
415
|
-
pkcs1.verify(cert, chain, store, data)
|
416
|
-
|
417
|
-
else
|
418
|
-
raise NotImplementedError, "Unsupported signature method #{method.inspect}"
|
419
|
-
end
|
420
|
-
end
|
476
|
+
def initialize(hash = {}, parser = nil)
|
477
|
+
set_indirect(false)
|
421
478
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
def self.required_size(method, certificate, key, ca)
|
426
|
-
self.compute(method, "", certificate, key, ca).size
|
427
|
-
end
|
479
|
+
super
|
480
|
+
end
|
481
|
+
end
|
428
482
|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
def self.compute(method, data, certificate, key, ca)
|
433
|
-
case method
|
434
|
-
when PKCS7_DETACHED
|
435
|
-
OpenSSL::PKCS7.sign(certificate, key, data, ca, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
|
483
|
+
class AppData < BuildData
|
484
|
+
field :REx, Type: String, Version: "1.6"
|
485
|
+
end
|
436
486
|
|
437
|
-
|
438
|
-
|
487
|
+
class SigQData < BuildData
|
488
|
+
field :Preview, Type: Boolean, Default: false, Version: "1.7"
|
489
|
+
end
|
439
490
|
|
440
|
-
|
441
|
-
|
491
|
+
class BuildProperties < Dictionary
|
492
|
+
include StandardObject
|
493
|
+
|
494
|
+
field :Filter, Type: BuildData, Version: "1.5"
|
495
|
+
field :PubSec, Type: BuildData, Version: "1.5"
|
496
|
+
field :App, Type: AppData, Version: "1.5"
|
497
|
+
field :SigQ, Type: SigQData, Version: "1.7"
|
498
|
+
|
499
|
+
def initialize(hash = {}, parser = nil)
|
500
|
+
set_indirect(false)
|
501
|
+
|
502
|
+
super
|
503
|
+
end
|
504
|
+
|
505
|
+
def pre_build # :nodoc:
|
506
|
+
self.Filter ||= BuildData.new
|
507
|
+
self.Filter.Name ||= :'Adobe.PPKLite'
|
508
|
+
self.Filter.R ||= 0x20020
|
509
|
+
self.Filter.V ||= 2
|
510
|
+
self.Filter.Date ||= Time.now.to_s
|
511
|
+
|
512
|
+
self.PubSec ||= BuildData.new
|
513
|
+
self.PubSec.NonEFontNoWarn ||= true
|
514
|
+
self.PubSec.Date ||= Time.now.to_s
|
515
|
+
self.PubSec.R ||= 0x20021
|
516
|
+
|
517
|
+
self.App ||= AppData.new
|
518
|
+
self.App.Name ||= :Reader
|
519
|
+
self.App.REx = "11.0.8"
|
520
|
+
self.App.TrustedMode ||= true
|
521
|
+
self.App.OS ||= [:Win]
|
522
|
+
self.App.R ||= 0xb0008
|
523
|
+
|
524
|
+
super
|
525
|
+
end
|
526
|
+
end
|
442
527
|
|
443
|
-
|
444
|
-
|
445
|
-
|
528
|
+
#
|
529
|
+
# Class representing a digital signature.
|
530
|
+
#
|
531
|
+
class DigitalSignature < Dictionary
|
532
|
+
include StandardObject
|
533
|
+
|
534
|
+
add_type_signature Filter: :'Adobe.PPKLite'
|
535
|
+
add_type_signature Filter: :'Adobe.PPKMS'
|
536
|
+
|
537
|
+
field :Type, Type: Name, Default: :Sig
|
538
|
+
field :Filter, Type: Name, Default: :'Adobe.PPKLite', Required: true
|
539
|
+
field :SubFilter, Type: Name
|
540
|
+
field :Contents, Type: String, Required: true
|
541
|
+
field :Cert, Type: [String, Array.of(String)]
|
542
|
+
field :ByteRange, Type: Array.of(Integer, length: 4)
|
543
|
+
field :Reference, Type: Array.of(Reference), Version: "1.5"
|
544
|
+
field :Changes, Type: Array
|
545
|
+
field :Name, Type: String
|
546
|
+
field :M, Type: String
|
547
|
+
field :Location, Type: String
|
548
|
+
field :Reason, Type: String
|
549
|
+
field :ContactInfo, Type: String
|
550
|
+
field :R, Type: Integer
|
551
|
+
field :V, Type: Integer, Default: 0, Version: "1.5"
|
552
|
+
field :Prop_Build, Type: BuildProperties, Version: "1.5"
|
553
|
+
field :Prop_AuthTime, Type: Integer, Version: "1.5"
|
554
|
+
field :Prop_AuthType, Type: Name, Version: "1.5"
|
555
|
+
|
556
|
+
def pre_build # :nodoc:
|
557
|
+
self.M = Origami::Date.now
|
558
|
+
self.Prop_Build ||= BuildProperties.new.pre_build
|
559
|
+
|
560
|
+
super
|
561
|
+
end
|
562
|
+
|
563
|
+
def to_s(indent: nil, tab: nil, eol: nil) # :nodoc:
|
564
|
+
# Must be deterministic, ignore parameters
|
565
|
+
indent_value = 1
|
566
|
+
tab_value = "\t"
|
567
|
+
eol_value = $/
|
568
|
+
|
569
|
+
content = TOKENS.first + eol_value
|
570
|
+
|
571
|
+
to_a.sort_by { |key, _| key }.reverse_each do |key, value|
|
572
|
+
content << tab_value * indent_value << key.to_s << " "
|
573
|
+
content << (value.is_a?(Dictionary) ? value.to_s(indent: indent_value + 1) : value.to_s) << eol_value
|
446
574
|
end
|
447
575
|
|
448
|
-
|
449
|
-
# Class representing a signature which can be embedded in DigitalSignature dictionary.
|
450
|
-
# It must be a direct object.
|
451
|
-
#
|
452
|
-
class Reference < Dictionary
|
453
|
-
include StandardObject
|
576
|
+
content << tab_value * (indent_value - 1) << TOKENS.last
|
454
577
|
|
455
|
-
|
578
|
+
output(content)
|
579
|
+
end
|
456
580
|
|
457
|
-
|
458
|
-
|
459
|
-
field :TransformParams, :Type => Dictionary
|
460
|
-
field :Data, :Type => Object
|
461
|
-
field :DigestMethod, :Type => Name, :Default => :MD5
|
462
|
-
field :DigestValue, :Type => String
|
463
|
-
field :DigestLocation, :Type => Array
|
581
|
+
def ranges
|
582
|
+
byte_range = self.ByteRange
|
464
583
|
|
465
|
-
|
466
|
-
|
584
|
+
unless byte_range.is_a?(Array) && (byte_range.length == 4) && byte_range.all? { |i| i.is_a?(Integer) }
|
585
|
+
raise SignatureError, "Invalid ByteRange field value"
|
586
|
+
end
|
467
587
|
|
468
|
-
|
469
|
-
|
588
|
+
byte_range.map(&:to_i).each_slice(2).map do |start, length|
|
589
|
+
(start...start + length)
|
470
590
|
end
|
591
|
+
end
|
471
592
|
|
472
|
-
|
473
|
-
|
593
|
+
def signature_data
|
594
|
+
raise SignatureError, "Invalid signature data" unless self[:Contents].is_a?(String)
|
474
595
|
|
475
|
-
|
476
|
-
|
477
|
-
field :R, :Type => Number, :Version => "1.5"
|
478
|
-
field :PreRelease, :Type => Boolean, :Default => false, :Version => "1.5"
|
479
|
-
field :OS, :Type => Array, :Version => "1.5"
|
480
|
-
field :NonEFontNoWarn, :Type => Boolean, :Version => "1.5"
|
481
|
-
field :TrustedMode, :Type => Boolean, :Version => "1.5"
|
482
|
-
field :V, :Type => Number, :Version => "1.5"
|
596
|
+
self[:Contents]
|
597
|
+
end
|
483
598
|
|
484
|
-
|
485
|
-
|
599
|
+
def certificate_chain
|
600
|
+
return [] unless key?(:Cert)
|
486
601
|
|
487
|
-
|
488
|
-
|
602
|
+
chain = self.Cert
|
603
|
+
if !chain.is_a?(String) && !(chain.is_a?(Array) && chain.all? { |cert| cert.is_a?(String) })
|
604
|
+
return SignatureError, "Invalid embedded certificate chain"
|
489
605
|
end
|
490
606
|
|
491
|
-
|
492
|
-
|
493
|
-
end
|
607
|
+
[chain].flatten.map! { |str| OpenSSL::X509::Certificate.new(str) }
|
608
|
+
end
|
494
609
|
|
495
|
-
|
496
|
-
|
497
|
-
|
610
|
+
def signature_offset # :nodoc:
|
611
|
+
indent_value = 1
|
612
|
+
tab_value = "\t"
|
613
|
+
eol_value = $/
|
614
|
+
content = "#{no} #{generation} obj" + eol_value + TOKENS.first + eol_value
|
498
615
|
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
field :Filter, :Type => BuildData, :Version => "1.5"
|
503
|
-
field :PubSec, :Type => BuildData, :Version => "1.5"
|
504
|
-
field :App, :Type => AppData, :Version => "1.5"
|
505
|
-
field :SigQ, :Type => SigQData, :Version => "1.7"
|
506
|
-
|
507
|
-
def initialize(hash = {}, parser = nil)
|
508
|
-
set_indirect(false)
|
509
|
-
|
510
|
-
super(hash, parser)
|
511
|
-
end
|
512
|
-
|
513
|
-
def pre_build #:nodoc:
|
514
|
-
self.Filter ||= BuildData.new
|
515
|
-
self.Filter.Name ||= :"Adobe.PPKLite"
|
516
|
-
self.Filter.R ||= 0x20020
|
517
|
-
self.Filter.V ||= 2
|
518
|
-
self.Filter.Date ||= Time.now.to_s
|
519
|
-
|
520
|
-
self.PubSec ||= BuildData.new
|
521
|
-
self.PubSec.NonEFontNoWarn ||= true
|
522
|
-
self.PubSec.Date ||= Time.now.to_s
|
523
|
-
self.PubSec.R ||= 0x20021
|
524
|
-
|
525
|
-
self.App ||= AppData.new
|
526
|
-
self.App.Name ||= :Reader
|
527
|
-
self.App.REx = "11.0.8"
|
528
|
-
self.App.TrustedMode ||= true
|
529
|
-
self.App.OS ||= [ :Win ]
|
530
|
-
self.App.R ||= 0xb0008
|
531
|
-
|
532
|
-
super
|
533
|
-
end
|
534
|
-
end
|
616
|
+
to_a.sort_by { |key, _| key }.reverse_each do |key, value|
|
617
|
+
if key == :Contents
|
618
|
+
content << tab_value * indent_value + key.to_s + " "
|
535
619
|
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
add_type_signature :Filter => :"Adobe.PPKLite"
|
543
|
-
add_type_signature :Filter => :"Adobe.PPKMS"
|
544
|
-
|
545
|
-
field :Type, :Type => Name, :Default => :Sig
|
546
|
-
field :Filter, :Type => Name, :Default => :"Adobe.PPKLite", :Required => true
|
547
|
-
field :SubFilter, :Type => Name
|
548
|
-
field :Contents, :Type => String, :Required => true
|
549
|
-
field :Cert, :Type => [ String, Array.of(String) ]
|
550
|
-
field :ByteRange, :Type => Array.of(Integer, length: 4)
|
551
|
-
field :Reference, :Type => Array.of(Reference), :Version => "1.5"
|
552
|
-
field :Changes, :Type => Array
|
553
|
-
field :Name, :Type => String
|
554
|
-
field :M, :Type => String
|
555
|
-
field :Location, :Type => String
|
556
|
-
field :Reason, :Type => String
|
557
|
-
field :ContactInfo, :Type => String
|
558
|
-
field :R, :Type => Integer
|
559
|
-
field :V, :Type => Integer, :Default => 0, :Version => "1.5"
|
560
|
-
field :Prop_Build, :Type => BuildProperties, :Version => "1.5"
|
561
|
-
field :Prop_AuthTime, :Type => Integer, :Version => "1.5"
|
562
|
-
field :Prop_AuthType, :Type => Name, :Version => "1.5"
|
563
|
-
|
564
|
-
def pre_build #:nodoc:
|
565
|
-
self.M = Origami::Date.now
|
566
|
-
self.Prop_Build ||= BuildProperties.new.pre_build
|
567
|
-
|
568
|
-
super
|
569
|
-
end
|
570
|
-
|
571
|
-
def to_s(indent: 1, tab: "\t", eol: $/) #:nodoc:
|
572
|
-
|
573
|
-
# Must be deterministic.
|
574
|
-
indent, tab, eol = 1, "\t", $/
|
575
|
-
|
576
|
-
content = TOKENS.first + eol
|
577
|
-
|
578
|
-
self.to_a.sort_by{ |key, _| key }.reverse_each do |key, value|
|
579
|
-
content << tab * indent << key.to_s << " "
|
580
|
-
content << (value.is_a?(Dictionary) ? value.to_s(indent: indent + 1) : value.to_s) << eol
|
581
|
-
end
|
582
|
-
|
583
|
-
content << tab * (indent - 1) << TOKENS.last
|
584
|
-
|
585
|
-
output(content)
|
586
|
-
end
|
587
|
-
|
588
|
-
def ranges
|
589
|
-
byte_range = self.ByteRange
|
590
|
-
|
591
|
-
unless byte_range.is_a?(Array) and byte_range.length == 4 and byte_range.all? {|i| i.is_a?(Integer) }
|
592
|
-
raise SignatureError, "Invalid ByteRange field value"
|
593
|
-
end
|
594
|
-
|
595
|
-
byte_range.map(&:to_i).each_slice(2).map do |start, length|
|
596
|
-
(start...start + length)
|
597
|
-
end
|
598
|
-
end
|
599
|
-
|
600
|
-
def signature_data
|
601
|
-
raise SignatureError, "Invalid signature data" unless self[:Contents].is_a?(String)
|
602
|
-
|
603
|
-
self[:Contents]
|
604
|
-
end
|
605
|
-
|
606
|
-
def certificate_chain
|
607
|
-
return [] unless key?(:Cert)
|
608
|
-
|
609
|
-
chain = self.Cert
|
610
|
-
unless chain.is_a?(String) or (chain.is_a?(Array) and chain.all?{|cert| cert.is_a?(String)})
|
611
|
-
return SignatureError, "Invalid embedded certificate chain"
|
612
|
-
end
|
613
|
-
|
614
|
-
[ chain ].flatten.map! {|str| OpenSSL::X509::Certificate.new(str) }
|
615
|
-
end
|
616
|
-
|
617
|
-
def signature_offset #:nodoc:
|
618
|
-
indent, tab, eol = 1, "\t", $/
|
619
|
-
content = "#{no} #{generation} obj" + eol + TOKENS.first + eol
|
620
|
-
|
621
|
-
self.to_a.sort_by{ |key, _| key }.reverse_each do |key, value|
|
622
|
-
if key == :Contents
|
623
|
-
content << tab * indent + key.to_s + " "
|
624
|
-
|
625
|
-
return content.size
|
626
|
-
else
|
627
|
-
content << tab * indent + key.to_s << " "
|
628
|
-
content << (value.is_a?(Dictionary) ? value.to_s(indent: indent + 1) : value.to_s) << eol
|
629
|
-
end
|
630
|
-
end
|
631
|
-
|
632
|
-
nil
|
633
|
-
end
|
620
|
+
return content.size
|
621
|
+
else
|
622
|
+
content << tab_value * indent_value + key.to_s << " "
|
623
|
+
content << (value.is_a?(Dictionary) ? value.to_s(indent: indent_value + 1) : value.to_s) << eol_value
|
624
|
+
end
|
634
625
|
end
|
635
626
|
|
627
|
+
nil
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
module UsageRights
|
633
|
+
module Rights
|
634
|
+
DOCUMENT_FULLSAVE = %i[Document FullSave]
|
635
|
+
DOCUMENT_ALL = DOCUMENT_FULLSAVE
|
636
|
+
|
637
|
+
ANNOTS_CREATE = %i[Annots Create]
|
638
|
+
ANNOTS_DELETE = %i[Annots Delete]
|
639
|
+
ANNOTS_MODIFY = %i[Annots Modify]
|
640
|
+
ANNOTS_COPY = %i[Annots Copy]
|
641
|
+
ANNOTS_IMPORT = %i[Annots Import]
|
642
|
+
ANNOTS_EXPORT = %i[Annots Export]
|
643
|
+
ANNOTS_ONLINE = %i[Annots Online]
|
644
|
+
ANNOTS_SUMMARYVIEW = %i[Annots SummaryView]
|
645
|
+
ANNOTS_ALL = %i[Annots Create Modify Copy Import Export Online SummaryView]
|
646
|
+
|
647
|
+
FORM_FILLIN = %i[Form FillIn]
|
648
|
+
FORM_IMPORT = %i[Form Import]
|
649
|
+
FORM_EXPORT = %i[Form Export]
|
650
|
+
FORM_SUBMITSTANDALONE = %i[Form SubmitStandAlone]
|
651
|
+
FORM_SPAWNTEMPLATE = %i[Form SpawnTemplate]
|
652
|
+
FORM_BARCODEPLAINTEXT = %i[Form BarcodePlaintext]
|
653
|
+
FORM_ONLINE = %i[Form Online]
|
654
|
+
FORM_ALL = %i[Form FillIn Import Export SubmitStandAlone SpawnTemplate BarcodePlaintext Online]
|
655
|
+
|
656
|
+
FORMEX_BARCODEPLAINTEXT = %i[FormEx BarcodePlaintext]
|
657
|
+
FORMEX_ALL = FORMEX_BARCODEPLAINTEXT
|
658
|
+
|
659
|
+
SIGNATURE_MODIFY = %i[Signature Modify]
|
660
|
+
SIGNATURE_ALL = SIGNATURE_MODIFY
|
661
|
+
|
662
|
+
EF_CREATE = %i[EF Create]
|
663
|
+
EF_DELETE = %i[EF Delete]
|
664
|
+
EF_MODIFY = %i[EF Modify]
|
665
|
+
EF_IMPORT = %i[EF Import]
|
666
|
+
EF_ALL = %i[EF Create Delete Modify Import]
|
667
|
+
|
668
|
+
ALL = [DOCUMENT_ALL, ANNOTS_ALL, FORM_ALL, SIGNATURE_ALL, EF_ALL]
|
636
669
|
end
|
637
670
|
|
638
|
-
|
639
|
-
|
640
|
-
module Rights
|
641
|
-
DOCUMENT_FULLSAVE = %i[Document FullSave]
|
642
|
-
DOCUMENT_ALL = DOCUMENT_FULLSAVE
|
643
|
-
|
644
|
-
ANNOTS_CREATE = %i[Annots Create]
|
645
|
-
ANNOTS_DELETE = %i[Annots Delete]
|
646
|
-
ANNOTS_MODIFY = %i[Annots Modify]
|
647
|
-
ANNOTS_COPY = %i[Annots Copy]
|
648
|
-
ANNOTS_IMPORT = %i[Annots Import]
|
649
|
-
ANNOTS_EXPORT = %i[Annots Export]
|
650
|
-
ANNOTS_ONLINE = %i[Annots Online]
|
651
|
-
ANNOTS_SUMMARYVIEW = %i[Annots SummaryView]
|
652
|
-
ANNOTS_ALL = %i[Annots Create Modify Copy Import Export Online SummaryView]
|
653
|
-
|
654
|
-
FORM_FILLIN = %i[Form FillIn]
|
655
|
-
FORM_IMPORT = %i[Form Import]
|
656
|
-
FORM_EXPORT = %i[Form Export]
|
657
|
-
FORM_SUBMITSTANDALONE = %i[Form SubmitStandAlone]
|
658
|
-
FORM_SPAWNTEMPLATE = %i[Form SpawnTemplate]
|
659
|
-
FORM_BARCODEPLAINTEXT = %i[Form BarcodePlaintext]
|
660
|
-
FORM_ONLINE = %i[Form Online]
|
661
|
-
FORM_ALL = %i[Form FillIn Import Export SubmitStandAlone SpawnTemplate BarcodePlaintext Online]
|
662
|
-
|
663
|
-
FORMEX_BARCODEPLAINTEXT = %i[FormEx BarcodePlaintext]
|
664
|
-
FORMEX_ALL = FORMEX_BARCODEPLAINTEXT
|
665
|
-
|
666
|
-
SIGNATURE_MODIFY = %i[Signature Modify]
|
667
|
-
SIGNATURE_ALL = SIGNATURE_MODIFY
|
668
|
-
|
669
|
-
EF_CREATE = %i[EF Create]
|
670
|
-
EF_DELETE = %i[EF Delete]
|
671
|
-
EF_MODIFY = %i[EF Modify]
|
672
|
-
EF_IMPORT = %i[EF Import]
|
673
|
-
EF_ALL = %i[EF Create Delete Modify Import]
|
674
|
-
|
675
|
-
ALL = [ DOCUMENT_ALL, ANNOTS_ALL, FORM_ALL, SIGNATURE_ALL, EF_ALL ]
|
676
|
-
end
|
677
|
-
|
678
|
-
class TransformParams < Dictionary
|
679
|
-
include StandardObject
|
671
|
+
class TransformParams < Dictionary
|
672
|
+
include StandardObject
|
680
673
|
|
681
|
-
|
674
|
+
VERSION = Name.new("2.2")
|
682
675
|
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
676
|
+
field :Type, Type: Name, Default: :TransformParams
|
677
|
+
field :Document, Type: Array.of(Name)
|
678
|
+
field :Msg, Type: String
|
679
|
+
field :V, Type: Name, Default: VERSION
|
680
|
+
field :Annots, Type: Array.of(Name)
|
681
|
+
field :Form, Type: Array.of(Name)
|
682
|
+
field :FormEx, Type: Array.of(Name)
|
683
|
+
field :Signature, Type: Array.of(Name)
|
684
|
+
field :EF, Type: Array.of(Name), Version: "1.6"
|
685
|
+
field :P, Type: Boolean, Default: false, Version: "1.6"
|
693
686
|
|
694
|
-
|
695
|
-
|
687
|
+
def initialize(hash = {}, parser = nil)
|
688
|
+
set_indirect(false)
|
696
689
|
|
697
|
-
|
698
|
-
|
699
|
-
end
|
690
|
+
super
|
691
|
+
end
|
700
692
|
end
|
701
|
-
|
693
|
+
end
|
702
694
|
end
|