hexapdf 0.28.0 → 0.31.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 +86 -10
- data/examples/024-digital-signatures.rb +23 -0
- data/lib/hexapdf/cli/command.rb +16 -1
- data/lib/hexapdf/cli/info.rb +9 -1
- data/lib/hexapdf/cli/inspect.rb +2 -2
- data/lib/hexapdf/composer.rb +76 -28
- data/lib/hexapdf/configuration.rb +29 -16
- data/lib/hexapdf/dictionary_fields.rb +13 -4
- data/lib/hexapdf/digital_signature/cms_handler.rb +137 -0
- data/lib/hexapdf/digital_signature/handler.rb +138 -0
- data/lib/hexapdf/digital_signature/pkcs1_handler.rb +96 -0
- data/lib/hexapdf/{type → digital_signature}/signature.rb +3 -8
- data/lib/hexapdf/digital_signature/signatures.rb +210 -0
- data/lib/hexapdf/digital_signature/signing/default_handler.rb +317 -0
- data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +308 -0
- data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +148 -0
- data/lib/hexapdf/digital_signature/signing.rb +101 -0
- data/lib/hexapdf/{type/signature → digital_signature}/verification_result.rb +37 -41
- data/lib/hexapdf/digital_signature.rb +56 -0
- data/lib/hexapdf/document/pages.rb +31 -18
- data/lib/hexapdf/document.rb +29 -15
- data/lib/hexapdf/encryption/standard_security_handler.rb +4 -3
- data/lib/hexapdf/filter/flate_decode.rb +20 -8
- data/lib/hexapdf/layout/page_style.rb +144 -0
- data/lib/hexapdf/layout.rb +1 -0
- data/lib/hexapdf/task/optimize.rb +8 -6
- data/lib/hexapdf/type/font_simple.rb +14 -2
- data/lib/hexapdf/type/object_stream.rb +7 -2
- data/lib/hexapdf/type/outline.rb +1 -1
- data/lib/hexapdf/type/outline_item.rb +1 -1
- data/lib/hexapdf/type/page.rb +29 -8
- data/lib/hexapdf/type/xref_stream.rb +11 -4
- data/lib/hexapdf/type.rb +0 -1
- data/lib/hexapdf/version.rb +1 -1
- data/lib/hexapdf/writer.rb +1 -1
- data/test/hexapdf/{type/signature → digital_signature}/common.rb +31 -3
- data/test/hexapdf/digital_signature/signing/test_default_handler.rb +162 -0
- data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +225 -0
- data/test/hexapdf/digital_signature/signing/test_timestamp_handler.rb +88 -0
- data/test/hexapdf/{type/signature/test_adbe_pkcs7_detached.rb → digital_signature/test_cms_handler.rb} +7 -7
- data/test/hexapdf/{type/signature → digital_signature}/test_handler.rb +4 -4
- data/test/hexapdf/{type/signature/test_adbe_x509_rsa_sha1.rb → digital_signature/test_pkcs1_handler.rb} +3 -3
- data/test/hexapdf/{type → digital_signature}/test_signature.rb +7 -7
- data/test/hexapdf/digital_signature/test_signatures.rb +137 -0
- data/test/hexapdf/digital_signature/test_signing.rb +53 -0
- data/test/hexapdf/{type/signature → digital_signature}/test_verification_result.rb +7 -7
- data/test/hexapdf/document/test_pages.rb +25 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +2 -2
- data/test/hexapdf/filter/test_flate_decode.rb +19 -5
- data/test/hexapdf/layout/test_page_style.rb +70 -0
- data/test/hexapdf/task/test_optimize.rb +11 -9
- data/test/hexapdf/test_composer.rb +35 -10
- data/test/hexapdf/test_dictionary_fields.rb +9 -3
- data/test/hexapdf/test_document.rb +1 -1
- data/test/hexapdf/test_writer.rb +8 -8
- data/test/hexapdf/type/test_font_simple.rb +18 -6
- data/test/hexapdf/type/test_object_stream.rb +16 -7
- data/test/hexapdf/type/test_outline.rb +3 -1
- data/test/hexapdf/type/test_outline_item.rb +3 -1
- data/test/hexapdf/type/test_page.rb +42 -11
- data/test/hexapdf/type/test_xref_stream.rb +6 -1
- metadata +27 -15
- data/lib/hexapdf/document/signatures.rb +0 -546
- data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +0 -135
- data/lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb +0 -95
- data/lib/hexapdf/type/signature/handler.rb +0 -140
- data/test/hexapdf/document/test_signatures.rb +0 -352
|
@@ -1,352 +0,0 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
require 'test_helper'
|
|
4
|
-
require 'stringio'
|
|
5
|
-
require 'tempfile'
|
|
6
|
-
require 'hexapdf/document'
|
|
7
|
-
require_relative '../type/signature/common'
|
|
8
|
-
|
|
9
|
-
describe HexaPDF::Document::Signatures do
|
|
10
|
-
before do
|
|
11
|
-
@doc = HexaPDF::Document.new
|
|
12
|
-
@form = @doc.acro_form(create: true)
|
|
13
|
-
@sig1 = @form.create_signature_field("test1")
|
|
14
|
-
@sig2 = @form.create_signature_field("test2")
|
|
15
|
-
@handler = HexaPDF::Document::Signatures::DefaultHandler.new(
|
|
16
|
-
certificate: CERTIFICATES.signer_certificate,
|
|
17
|
-
key: CERTIFICATES.signer_key,
|
|
18
|
-
certificate_chain: [CERTIFICATES.ca_certificate]
|
|
19
|
-
)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
it "allows embedding an external signature value" do
|
|
23
|
-
doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
|
|
24
|
-
io = StringIO.new(''.b)
|
|
25
|
-
doc.signatures.add(io, @handler)
|
|
26
|
-
doc = HexaPDF::Document.new(io: io)
|
|
27
|
-
io = StringIO.new(''.b)
|
|
28
|
-
|
|
29
|
-
byte_range = nil
|
|
30
|
-
@handler.signature_size = 5000
|
|
31
|
-
@handler.external_signing = proc {|_, br| byte_range = br; "" }
|
|
32
|
-
doc.signatures.add(io, @handler)
|
|
33
|
-
|
|
34
|
-
io.pos = byte_range[0]
|
|
35
|
-
data = io.read(byte_range[1])
|
|
36
|
-
io.pos = byte_range[2]
|
|
37
|
-
data << io.read(byte_range[3])
|
|
38
|
-
contents = OpenSSL::PKCS7.sign(@handler.certificate, @handler.key, data, @handler.certificate_chain,
|
|
39
|
-
OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
|
|
40
|
-
HexaPDF::Document::Signatures.embed_signature(io, contents)
|
|
41
|
-
doc = HexaPDF::Document.new(io: io)
|
|
42
|
-
assert_equal(2, doc.signatures.each.count)
|
|
43
|
-
doc.signatures.each do |signature|
|
|
44
|
-
assert(signature.verify(allow_self_signed: true).messages.find {|m| m.content == 'Signature valid' })
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
describe "DefaultHandler" do
|
|
49
|
-
it "returns the size of serialized signature" do
|
|
50
|
-
assert_equal(1310, @handler.signature_size)
|
|
51
|
-
@handler.signature_size = 100
|
|
52
|
-
assert_equal(100, @handler.signature_size)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it "allows setting the DocMDP permissions" do
|
|
56
|
-
assert_nil(@handler.doc_mdp_permissions)
|
|
57
|
-
|
|
58
|
-
@handler.doc_mdp_permissions = :no_changes
|
|
59
|
-
assert_equal(1, @handler.doc_mdp_permissions)
|
|
60
|
-
@handler.doc_mdp_permissions = 1
|
|
61
|
-
assert_equal(1, @handler.doc_mdp_permissions)
|
|
62
|
-
|
|
63
|
-
@handler.doc_mdp_permissions = :form_filling
|
|
64
|
-
assert_equal(2, @handler.doc_mdp_permissions)
|
|
65
|
-
@handler.doc_mdp_permissions = 2
|
|
66
|
-
assert_equal(2, @handler.doc_mdp_permissions)
|
|
67
|
-
|
|
68
|
-
@handler.doc_mdp_permissions = :form_filling_and_annotations
|
|
69
|
-
assert_equal(3, @handler.doc_mdp_permissions)
|
|
70
|
-
@handler.doc_mdp_permissions = 3
|
|
71
|
-
assert_equal(3, @handler.doc_mdp_permissions)
|
|
72
|
-
|
|
73
|
-
@handler.doc_mdp_permissions = nil
|
|
74
|
-
assert_nil(@handler.doc_mdp_permissions)
|
|
75
|
-
|
|
76
|
-
assert_raises(ArgumentError) { @handler.doc_mdp_permissions = :other }
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
describe "sign" do
|
|
80
|
-
it "can sign the data using PKCS7" do
|
|
81
|
-
data = StringIO.new("data")
|
|
82
|
-
store = OpenSSL::X509::Store.new
|
|
83
|
-
store.add_cert(CERTIFICATES.ca_certificate)
|
|
84
|
-
|
|
85
|
-
pkcs7 = OpenSSL::PKCS7.new(@handler.sign(data, [0, 4, 0, 0]))
|
|
86
|
-
assert(pkcs7.detached?)
|
|
87
|
-
assert_equal([CERTIFICATES.signer_certificate, CERTIFICATES.ca_certificate],
|
|
88
|
-
pkcs7.certificates)
|
|
89
|
-
assert(pkcs7.verify([], store, data.string, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY))
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
it "can use external signing" do
|
|
93
|
-
@handler.external_signing = proc { "hallo" }
|
|
94
|
-
assert_equal("hallo", @handler.sign(StringIO.new, [0, 0, 0, 0]))
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
describe "finalize_objects" do
|
|
99
|
-
before do
|
|
100
|
-
@field = @doc.wrap({})
|
|
101
|
-
@obj = @doc.wrap({})
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
it "does nothing if no finalization tasks need to be done" do
|
|
105
|
-
@handler.finalize_objects(@field, @obj)
|
|
106
|
-
assert(@field.empty?)
|
|
107
|
-
assert(@obj.empty?)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
it "adjust the /SubFilter if signature type is etsi" do
|
|
111
|
-
@handler.signature_type = :etsi
|
|
112
|
-
@handler.finalize_objects(@field, @obj)
|
|
113
|
-
assert_equal(:'ETSI.CAdES.detached', @obj[:SubFilter])
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
it "sets the reason, location and contact info fields" do
|
|
117
|
-
@handler.reason = 'Reason'
|
|
118
|
-
@handler.location = 'Location'
|
|
119
|
-
@handler.contact_info = 'Contact'
|
|
120
|
-
@handler.finalize_objects(@field, @obj)
|
|
121
|
-
assert(@field.empty?)
|
|
122
|
-
assert_equal({Reason: 'Reason', Location: 'Location', ContactInfo: 'Contact'}, @obj.value)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
it "applies the specified DocMDP permissions" do
|
|
126
|
-
@handler.doc_mdp_permissions = :no_changes
|
|
127
|
-
@handler.finalize_objects(@field, @obj)
|
|
128
|
-
ref = @obj[:Reference][0]
|
|
129
|
-
assert_equal(:DocMDP, ref[:TransformMethod])
|
|
130
|
-
assert_equal(:SHA1, ref[:DigestMethod])
|
|
131
|
-
assert_equal(1, ref[:TransformParams][:P])
|
|
132
|
-
assert_equal(:'1.2', ref[:TransformParams][:V])
|
|
133
|
-
assert_same(@obj, @doc.catalog[:Perms][:DocMDP])
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
it "fails if DocMDP should be set but there is already a signature" do
|
|
137
|
-
@handler.doc_mdp_permissions = :no_changes
|
|
138
|
-
2.times do
|
|
139
|
-
field = @doc.acro_form(create: true).create_signature_field('test')
|
|
140
|
-
field.field_value = :something
|
|
141
|
-
end
|
|
142
|
-
assert_raises(HexaPDF::Error) { @handler.finalize_objects(@field, @obj) }
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
describe "TimestampHandler" do
|
|
148
|
-
before do
|
|
149
|
-
@handler = HexaPDF::Document::Signatures::TimestampHandler.new
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
it "allows setting the attributes in the constructor" do
|
|
153
|
-
handler = HexaPDF::Document::Signatures::TimestampHandler.new(
|
|
154
|
-
tsa_url: "url", tsa_hash_algorithm: "MD5", tsa_policy_id: "5",
|
|
155
|
-
reason: "Reason", location: "Location", contact_info: "Contact",
|
|
156
|
-
signature_size: 1_000
|
|
157
|
-
)
|
|
158
|
-
assert_equal("url", handler.tsa_url)
|
|
159
|
-
assert_equal("MD5", handler.tsa_hash_algorithm)
|
|
160
|
-
assert_equal("5", handler.tsa_policy_id)
|
|
161
|
-
assert_equal("Reason", handler.reason)
|
|
162
|
-
assert_equal("Location", handler.location)
|
|
163
|
-
assert_equal("Contact", handler.contact_info)
|
|
164
|
-
assert_equal(1_000, handler.signature_size)
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
it "finalizes the signature field and signature objects" do
|
|
168
|
-
@field = @doc.wrap({})
|
|
169
|
-
@sig = @doc.wrap({})
|
|
170
|
-
@handler.reason = 'Reason'
|
|
171
|
-
@handler.location = 'Location'
|
|
172
|
-
@handler.contact_info = 'Contact'
|
|
173
|
-
|
|
174
|
-
@handler.finalize_objects(@field, @sig)
|
|
175
|
-
assert_equal('2.0', @doc.version)
|
|
176
|
-
assert_equal(:DocTimeStamp, @sig[:Type])
|
|
177
|
-
assert_equal(:'ETSI.RFC3161', @sig[:SubFilter])
|
|
178
|
-
assert_equal('Reason', @sig[:Reason])
|
|
179
|
-
assert_equal('Location', @sig[:Location])
|
|
180
|
-
assert_equal('Contact', @sig[:ContactInfo])
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
it "returns the size of serialized signature" do
|
|
184
|
-
@handler.tsa_url = "http://127.0.0.1:34567"
|
|
185
|
-
CERTIFICATES.start_tsa_server
|
|
186
|
-
assert_equal(1420, @handler.signature_size)
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
describe "sign" do
|
|
190
|
-
before do
|
|
191
|
-
@data = StringIO.new("data")
|
|
192
|
-
@range = [0, 4, 0, 0]
|
|
193
|
-
@handler.tsa_url = "http://127.0.0.1:34567"
|
|
194
|
-
CERTIFICATES.start_tsa_server
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
it "respects the set hash algorithm and policy id" do
|
|
198
|
-
@handler.tsa_hash_algorithm = 'SHA256'
|
|
199
|
-
@handler.tsa_policy_id = '1.2.3.4.2'
|
|
200
|
-
token = OpenSSL::ASN1.decode(@handler.sign(@data, @range))
|
|
201
|
-
content = OpenSSL::ASN1.decode(token.value[1].value[0].value[2].value[1].value[0].value)
|
|
202
|
-
policy_id = content.value[1].value
|
|
203
|
-
digest_algorithm = content.value[2].value[0].value[0].value
|
|
204
|
-
assert_equal('SHA256', digest_algorithm)
|
|
205
|
-
assert_equal("1.2.3.4.2", policy_id)
|
|
206
|
-
end
|
|
207
|
-
|
|
208
|
-
it "returns the serialized timestamp token" do
|
|
209
|
-
token = OpenSSL::PKCS7.new(@handler.sign(@data, @range))
|
|
210
|
-
assert_equal(CERTIFICATES.ca_certificate.subject, token.signers[0].issuer)
|
|
211
|
-
assert_equal(CERTIFICATES.timestamp_certificate.serial, token.signers[0].serial)
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
it "fails if the timestamp token could not be created" do
|
|
215
|
-
@handler.tsa_hash_algorithm = 'SHA1'
|
|
216
|
-
msg = assert_raises(HexaPDF::Error) { @handler.sign(@data, @range) }
|
|
217
|
-
assert_match(/BAD_ALG/, msg.message)
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
it "fails if the timestamp server couldn't process the request" do
|
|
221
|
-
@handler.tsa_policy_id = '1.2.3.4.1'
|
|
222
|
-
msg = assert_raises(HexaPDF::Error) { @handler.sign(@data, @range) }
|
|
223
|
-
assert_match(/Invalid TSA server response/, msg.message)
|
|
224
|
-
end
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
it "iterates over all signature dictionaries" do
|
|
229
|
-
assert_equal([], @doc.signatures.to_a)
|
|
230
|
-
@sig1.field_value = :sig1
|
|
231
|
-
@sig2.field_value = :sig2
|
|
232
|
-
assert_equal([:sig1, :sig2], @doc.signatures.to_a)
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
it "returns the number of signature dictionaries" do
|
|
236
|
-
@sig1.field_value = :sig1
|
|
237
|
-
assert_equal(1, @doc.signatures.count)
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
describe "handler" do
|
|
241
|
-
it "return the initialized handler" do
|
|
242
|
-
handler = @doc.signatures.handler(certificate: 'cert', reason: 'reason')
|
|
243
|
-
assert_equal('cert', handler.certificate)
|
|
244
|
-
assert_equal('reason', handler.reason)
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
it "fails if the given task is not available" do
|
|
248
|
-
assert_raises(HexaPDF::Error) { @doc.signatures.handler(name: :unknown) }
|
|
249
|
-
end
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
describe "add" do
|
|
253
|
-
before do
|
|
254
|
-
@doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
|
|
255
|
-
@io = StringIO.new(''.b)
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
it "uses the provided signature dictionary" do
|
|
259
|
-
sig = @doc.add({Type: :Sig, Key: :value})
|
|
260
|
-
@doc.signatures.add(@io, @handler, signature: sig)
|
|
261
|
-
assert_equal(1, @doc.signatures.to_a.compact.size)
|
|
262
|
-
assert_equal(:value, @doc.signatures.to_a[0][:Key])
|
|
263
|
-
refute_equal(:value, @doc.acro_form.each_field.first[:Key])
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
it "creates the signature dictionary if none is provided" do
|
|
267
|
-
@doc.signatures.add(@io, @handler)
|
|
268
|
-
assert_equal(1, @doc.signatures.to_a.compact.size)
|
|
269
|
-
refute(@doc.acro_form.each_field.first.key?(:Contents))
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
it "sets the needed information on the signature dictionary" do
|
|
273
|
-
def @handler.finalize_objects(sigfield, sig)
|
|
274
|
-
sig[:key] = :sig
|
|
275
|
-
sigfield[:key] = :sig_field
|
|
276
|
-
end
|
|
277
|
-
@doc.signatures.add(@io, @handler, write_options: {update_fields: false})
|
|
278
|
-
sig = @doc.signatures.first
|
|
279
|
-
assert_equal(:'Adobe.PPKLite', sig[:Filter])
|
|
280
|
-
assert_equal(:'adbe.pkcs7.detached', sig[:SubFilter])
|
|
281
|
-
assert_equal([0, 996, 3618, 2501], sig[:ByteRange].value)
|
|
282
|
-
assert_equal(:sig, sig[:key])
|
|
283
|
-
assert_equal(:sig_field, @doc.acro_form.each_field.first[:key])
|
|
284
|
-
assert(sig.key?(:Contents))
|
|
285
|
-
assert(sig.key?(:M))
|
|
286
|
-
end
|
|
287
|
-
|
|
288
|
-
it "creates the main form dictionary if necessary" do
|
|
289
|
-
@doc.signatures.add(@io, @handler)
|
|
290
|
-
assert(@doc.acro_form)
|
|
291
|
-
assert_equal([:signatures_exist, :append_only], @doc.acro_form.signature_flags)
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
it "uses the provided signature field" do
|
|
295
|
-
field = @doc.acro_form(create: true).create_signature_field('Signature2')
|
|
296
|
-
@doc.signatures.add(@io, @handler, signature: field)
|
|
297
|
-
assert_nil(@doc.acro_form.field_by_name("Signature3"))
|
|
298
|
-
refute_nil(field.field_value)
|
|
299
|
-
assert_nil(@doc.signatures.first[:T])
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
it "uses an existing signature field if possible" do
|
|
303
|
-
field = @doc.acro_form(create: true).create_signature_field('Signature2')
|
|
304
|
-
field.field_value = sig = @doc.add({Type: :Sig, key: :value})
|
|
305
|
-
@doc.signatures.add(@io, @handler, signature: sig)
|
|
306
|
-
assert_nil(@doc.acro_form.field_by_name("Signature3"))
|
|
307
|
-
assert_same(sig, @doc.signatures.first)
|
|
308
|
-
end
|
|
309
|
-
|
|
310
|
-
it "creates the signature field if necessary" do
|
|
311
|
-
@doc.acro_form(create: true).create_text_field('Signature2')
|
|
312
|
-
@doc.signatures.add(@io, @handler)
|
|
313
|
-
field = @doc.acro_form.field_by_name("Signature3")
|
|
314
|
-
assert_equal(:Sig, field.field_type)
|
|
315
|
-
refute_nil(field.field_value)
|
|
316
|
-
assert_equal(1, field.each_widget.count)
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
it "handles different xref section types correctly when determing the offsets" do
|
|
320
|
-
@doc.delete(7)
|
|
321
|
-
sig = @doc.signatures.add(@io, @handler, write_options: {update_fields: false})
|
|
322
|
-
assert_equal([0, 988, 3610, 2483], sig[:ByteRange].value)
|
|
323
|
-
end
|
|
324
|
-
|
|
325
|
-
it "works if the signature object is the last object of the xref section" do
|
|
326
|
-
field = @doc.acro_form(create: true).create_signature_field('Signature2')
|
|
327
|
-
field.create_widget(@doc.pages[0], Rect: [0, 0, 0, 0])
|
|
328
|
-
sig = @doc.signatures.add(@io, @handler, signature: field, write_options: {update_fields: false})
|
|
329
|
-
assert_equal([0, 3095, 5717, 380], sig[:ByteRange].value)
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
it "allows writing to a file in addition to writing to an IO" do
|
|
333
|
-
tempfile = Tempfile.new('hexapdf-signature')
|
|
334
|
-
tempfile.close
|
|
335
|
-
@doc.signatures.add(tempfile.path, @handler)
|
|
336
|
-
doc = HexaPDF::Document.open(tempfile.path)
|
|
337
|
-
assert(doc.signatures.first.verify(allow_self_signed: true).success?)
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
it "adds a new revision with the signature" do
|
|
341
|
-
@doc.signatures.add(@io, @handler)
|
|
342
|
-
signed_doc = HexaPDF::Document.new(io: @io)
|
|
343
|
-
assert(signed_doc.signatures.first.verify)
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
it "fails if the reserved signature space is too small" do
|
|
347
|
-
def @handler.signature_size; 200; end
|
|
348
|
-
msg = assert_raises(HexaPDF::Error) { @doc.signatures.add(@io, @handler) }
|
|
349
|
-
assert_match(/space.*too small.*200 vs/, msg.message)
|
|
350
|
-
end
|
|
351
|
-
end
|
|
352
|
-
end
|