hexapdf 0.28.0 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -10
  3. data/examples/024-digital-signatures.rb +23 -0
  4. data/lib/hexapdf/configuration.rb +12 -12
  5. data/lib/hexapdf/dictionary_fields.rb +6 -2
  6. data/lib/hexapdf/digital_signature/cms_handler.rb +137 -0
  7. data/lib/hexapdf/digital_signature/handler.rb +138 -0
  8. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +96 -0
  9. data/lib/hexapdf/{type → digital_signature}/signature.rb +3 -8
  10. data/lib/hexapdf/digital_signature/signatures.rb +210 -0
  11. data/lib/hexapdf/digital_signature/signing/default_handler.rb +317 -0
  12. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +308 -0
  13. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +148 -0
  14. data/lib/hexapdf/digital_signature/signing.rb +101 -0
  15. data/lib/hexapdf/{type/signature → digital_signature}/verification_result.rb +37 -41
  16. data/lib/hexapdf/digital_signature.rb +56 -0
  17. data/lib/hexapdf/document.rb +21 -14
  18. data/lib/hexapdf/encryption/standard_security_handler.rb +2 -1
  19. data/lib/hexapdf/type.rb +0 -1
  20. data/lib/hexapdf/version.rb +1 -1
  21. data/test/hexapdf/{type/signature → digital_signature}/common.rb +31 -3
  22. data/test/hexapdf/digital_signature/signing/test_default_handler.rb +162 -0
  23. data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +225 -0
  24. data/test/hexapdf/digital_signature/signing/test_timestamp_handler.rb +88 -0
  25. data/test/hexapdf/{type/signature/test_adbe_pkcs7_detached.rb → digital_signature/test_cms_handler.rb} +7 -7
  26. data/test/hexapdf/{type/signature → digital_signature}/test_handler.rb +4 -4
  27. data/test/hexapdf/{type/signature/test_adbe_x509_rsa_sha1.rb → digital_signature/test_pkcs1_handler.rb} +3 -3
  28. data/test/hexapdf/{type → digital_signature}/test_signature.rb +7 -7
  29. data/test/hexapdf/digital_signature/test_signatures.rb +137 -0
  30. data/test/hexapdf/digital_signature/test_signing.rb +53 -0
  31. data/test/hexapdf/{type/signature → digital_signature}/test_verification_result.rb +7 -7
  32. data/test/hexapdf/test_dictionary_fields.rb +2 -1
  33. data/test/hexapdf/test_document.rb +1 -1
  34. data/test/hexapdf/test_writer.rb +3 -3
  35. metadata +25 -15
  36. data/lib/hexapdf/document/signatures.rb +0 -546
  37. data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +0 -135
  38. data/lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb +0 -95
  39. data/lib/hexapdf/type/signature/handler.rb +0 -140
  40. data/test/hexapdf/document/test_signatures.rb +0 -352
@@ -0,0 +1,88 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require_relative '../common'
6
+
7
+ describe HexaPDF::DigitalSignature::Signing::TimestampHandler do
8
+ before do
9
+ @doc = HexaPDF::Document.new
10
+ @handler = HexaPDF::DigitalSignature::Signing::TimestampHandler.new
11
+ end
12
+
13
+ it "allows setting the attributes in the constructor" do
14
+ handler = @handler.class.new(
15
+ tsa_url: "url", tsa_hash_algorithm: "MD5", tsa_policy_id: "5",
16
+ reason: "Reason", location: "Location", contact_info: "Contact",
17
+ signature_size: 1_000
18
+ )
19
+ assert_equal("url", handler.tsa_url)
20
+ assert_equal("MD5", handler.tsa_hash_algorithm)
21
+ assert_equal("5", handler.tsa_policy_id)
22
+ assert_equal("Reason", handler.reason)
23
+ assert_equal("Location", handler.location)
24
+ assert_equal("Contact", handler.contact_info)
25
+ assert_equal(1_000, handler.signature_size)
26
+ end
27
+
28
+ it "finalizes the signature field and signature objects" do
29
+ @field = @doc.wrap({})
30
+ @sig = @doc.wrap({})
31
+ @handler.reason = 'Reason'
32
+ @handler.location = 'Location'
33
+ @handler.contact_info = 'Contact'
34
+
35
+ @handler.finalize_objects(@field, @sig)
36
+ assert_equal('2.0', @doc.version)
37
+ assert_equal(:DocTimeStamp, @sig[:Type])
38
+ assert_equal(:'Adobe.PPKLite', @sig[:Filter])
39
+ assert_equal(:'ETSI.RFC3161', @sig[:SubFilter])
40
+ assert_equal('Reason', @sig[:Reason])
41
+ assert_equal('Location', @sig[:Location])
42
+ assert_equal('Contact', @sig[:ContactInfo])
43
+ end
44
+
45
+ it "returns the size of serialized signature" do
46
+ @handler.tsa_url = "http://127.0.0.1:34567"
47
+ CERTIFICATES.start_tsa_server
48
+ assert(@handler.signature_size > 1000)
49
+ end
50
+
51
+ describe "sign" do
52
+ before do
53
+ @data = StringIO.new("data")
54
+ @range = [0, 4, 0, 0]
55
+ @handler.tsa_url = "http://127.0.0.1:34567"
56
+ CERTIFICATES.start_tsa_server
57
+ end
58
+
59
+ it "respects the set hash algorithm and policy id" do
60
+ @handler.tsa_hash_algorithm = 'SHA256'
61
+ @handler.tsa_policy_id = '1.2.3.4.2'
62
+ token = OpenSSL::ASN1.decode(@handler.sign(@data, @range))
63
+ content = OpenSSL::ASN1.decode(token.value[1].value[0].value[2].value[1].value[0].value)
64
+ policy_id = content.value[1].value
65
+ digest_algorithm = content.value[2].value[0].value[0].value
66
+ assert_equal('SHA256', digest_algorithm)
67
+ assert_equal("1.2.3.4.2", policy_id)
68
+ end
69
+
70
+ it "returns the serialized timestamp token" do
71
+ token = OpenSSL::PKCS7.new(@handler.sign(@data, @range))
72
+ assert_equal(CERTIFICATES.ca_certificate.subject, token.signers[0].issuer)
73
+ assert_equal(CERTIFICATES.timestamp_certificate.serial, token.signers[0].serial)
74
+ end
75
+
76
+ it "fails if the timestamp token could not be created" do
77
+ @handler.tsa_hash_algorithm = 'SHA1'
78
+ msg = assert_raises(HexaPDF::Error) { @handler.sign(@data, @range) }
79
+ assert_match(/BAD_ALG/, msg.message)
80
+ end
81
+
82
+ it "fails if the timestamp server couldn't process the request" do
83
+ @handler.tsa_policy_id = '1.2.3.4.1'
84
+ msg = assert_raises(HexaPDF::Error) { @handler.sign(@data, @range) }
85
+ assert_match(/Invalid TSA server response/, msg.message)
86
+ end
87
+ end
88
+ end
@@ -3,10 +3,10 @@
3
3
  require 'digest'
4
4
  require 'test_helper'
5
5
  require_relative 'common'
6
- require 'hexapdf/type/signature'
6
+ require 'hexapdf/digital_signature'
7
7
  require 'ostruct'
8
8
 
9
- describe HexaPDF::Type::Signature::AdbePkcs7Detached do
9
+ describe HexaPDF::DigitalSignature::CMSHandler do
10
10
  before do
11
11
  @data = 'Some data'
12
12
  @dict = OpenStruct.new
@@ -15,11 +15,11 @@ describe HexaPDF::Type::Signature::AdbePkcs7Detached do
15
15
  OpenSSL::PKCS7::DETACHED)
16
16
  @dict.contents = @pkcs7.to_der
17
17
  @dict.signed_data = @data
18
- @handler = HexaPDF::Type::Signature::AdbePkcs7Detached.new(@dict)
18
+ @handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
19
19
  end
20
20
 
21
21
  it "returns the signer name" do
22
- assert_equal("signer", @handler.signer_name)
22
+ assert_equal("RSA signer", @handler.signer_name)
23
23
  end
24
24
 
25
25
  it "returns the signing time" do
@@ -60,7 +60,7 @@ describe HexaPDF::Type::Signature::AdbePkcs7Detached do
60
60
  @pkcs7.add_signer(OpenSSL::PKCS7::SignerInfo.new(CERTIFICATES.signer_certificate,
61
61
  CERTIFICATES.signer_key, 'SHA1'))
62
62
  @dict.contents = @pkcs7.to_der
63
- @handler = HexaPDF::Type::Signature::AdbePkcs7Detached.new(@dict)
63
+ @handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
64
64
  result = @handler.verify(@store)
65
65
  assert_equal(2, result.messages.size)
66
66
  assert_equal(:error, result.messages.first.type)
@@ -80,7 +80,7 @@ describe HexaPDF::Type::Signature::AdbePkcs7Detached do
80
80
  @data, [CERTIFICATES.ca_certificate],
81
81
  OpenSSL::PKCS7::DETACHED)
82
82
  @dict.contents = @pkcs7.to_der
83
- @handler = HexaPDF::Type::Signature::AdbePkcs7Detached.new(@dict)
83
+ @handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
84
84
  result = @handler.verify(@store)
85
85
  assert_equal(:error, result.messages.first.type)
86
86
  assert_match(/key usage is missing 'Digital Signature'/, result.messages.first.content)
@@ -110,7 +110,7 @@ describe HexaPDF::Type::Signature::AdbePkcs7Detached do
110
110
  res = fac.create_timestamp(CERTIFICATES.signer_key, CERTIFICATES.timestamp_certificate, req)
111
111
  @dict.contents = res.token.to_der
112
112
  @dict.signature_type = 'ETSI.RFC3161'
113
- @handler = HexaPDF::Type::Signature::AdbePkcs7Detached.new(@dict)
113
+ @handler = HexaPDF::DigitalSignature::CMSHandler.new(@dict)
114
114
 
115
115
  result = @handler.verify(@store)
116
116
  assert_equal(:info, result.messages.last.type)
@@ -1,17 +1,17 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  require 'test_helper'
4
- require 'hexapdf/type/signature'
4
+ require 'hexapdf/digital_signature'
5
5
  require 'hexapdf/document'
6
6
  require 'time'
7
7
  require 'ostruct'
8
8
 
9
- describe HexaPDF::Type::Signature::Handler do
9
+ describe HexaPDF::DigitalSignature::Handler do
10
10
  before do
11
11
  @time = Time.parse("2021-11-14 7:00")
12
12
  @dict = {Name: "handler", M: @time}
13
- @handler = HexaPDF::Type::Signature::Handler.new(@dict)
14
- @result = HexaPDF::Type::Signature::VerificationResult.new
13
+ @handler = HexaPDF::DigitalSignature::Handler.new(@dict)
14
+ @result = HexaPDF::DigitalSignature::VerificationResult.new
15
15
  end
16
16
 
17
17
  it "returns the signer name" do
@@ -2,10 +2,10 @@
2
2
 
3
3
  require 'test_helper'
4
4
  require_relative 'common'
5
- require 'hexapdf/type/signature'
5
+ require 'hexapdf/digital_signature'
6
6
  require 'ostruct'
7
7
 
8
- describe HexaPDF::Type::Signature::AdbeX509RsaSha1 do
8
+ describe HexaPDF::DigitalSignature::PKCS1Handler do
9
9
  before do
10
10
  @data = 'Some data'
11
11
  @dict = OpenStruct.new
@@ -14,7 +14,7 @@ describe HexaPDF::Type::Signature::AdbeX509RsaSha1 do
14
14
  @dict.contents = OpenSSL::ASN1::OctetString.new(encoded_data).to_der
15
15
  @dict.Cert = [CERTIFICATES.signer_certificate.to_der]
16
16
  def @dict.key?(*); true; end
17
- @handler = HexaPDF::Type::Signature::AdbeX509RsaSha1.new(@dict)
17
+ @handler = HexaPDF::DigitalSignature::PKCS1Handler.new(@dict)
18
18
  end
19
19
 
20
20
  it "returns the certificate chain" do
@@ -2,11 +2,11 @@
2
2
 
3
3
  require 'test_helper'
4
4
  require 'hexapdf/document'
5
- require 'hexapdf/type/signature'
6
- require_relative 'signature/common'
5
+ require 'hexapdf/digital_signature'
6
+ require_relative 'common'
7
7
  require 'stringio'
8
8
 
9
- describe HexaPDF::Type::Signature::TransformParams do
9
+ describe HexaPDF::DigitalSignature::Signature::TransformParams do
10
10
  before do
11
11
  @doc = HexaPDF::Document.new
12
12
  @params = @doc.add({Type: :TransformParams})
@@ -36,7 +36,7 @@ describe HexaPDF::Type::Signature::TransformParams do
36
36
  end
37
37
  end
38
38
 
39
- describe HexaPDF::Type::Signature::SignatureReference do
39
+ describe HexaPDF::DigitalSignature::Signature::SignatureReference do
40
40
  before do
41
41
  @doc = HexaPDF::Document.new
42
42
  @sigref = @doc.add({Type: :SigRef})
@@ -52,7 +52,7 @@ describe HexaPDF::Type::Signature::SignatureReference do
52
52
  end
53
53
  end
54
54
 
55
- describe HexaPDF::Type::Signature do
55
+ describe HexaPDF::DigitalSignature::Signature do
56
56
  before do
57
57
  @doc = HexaPDF::Document.new
58
58
  @sig = @doc.add({Type: :Sig, Filter: :'Adobe.PPKLite', SubFilter: :'ETSI.CAdES.detached'})
@@ -65,7 +65,7 @@ describe HexaPDF::Type::Signature do
65
65
  end
66
66
 
67
67
  it "returns the signer name" do
68
- assert_equal('signer', @sig.signer_name)
68
+ assert_equal('RSA signer', @sig.signer_name)
69
69
  end
70
70
 
71
71
  it "returns the signing time" do
@@ -88,7 +88,7 @@ describe HexaPDF::Type::Signature do
88
88
 
89
89
  describe "signature_handler" do
90
90
  it "returns the signature handler" do
91
- assert_kind_of(HexaPDF::Type::Signature::Handler, @sig.signature_handler)
91
+ assert_kind_of(HexaPDF::DigitalSignature::Handler, @sig.signature_handler)
92
92
  end
93
93
 
94
94
  it "fails if the required handler is not available" do
@@ -0,0 +1,137 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'stringio'
5
+ require 'tempfile'
6
+ require 'hexapdf/document'
7
+ require_relative 'common'
8
+
9
+ describe HexaPDF::DigitalSignature::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
+ end
16
+
17
+ it "iterates over all signature dictionaries" do
18
+ assert_equal([], @doc.signatures.to_a)
19
+ @sig1.field_value = :sig1
20
+ @sig2.field_value = :sig2
21
+ assert_equal([:sig1, :sig2], @doc.signatures.to_a)
22
+ end
23
+
24
+ it "returns the number of signature dictionaries" do
25
+ @sig1.field_value = :sig1
26
+ assert_equal(1, @doc.signatures.count)
27
+ end
28
+
29
+ describe "signing_handler" do
30
+ it "return the initialized handler" do
31
+ handler = @doc.signatures.signing_handler(certificate: 'cert', reason: 'reason')
32
+ assert_equal('cert', handler.certificate)
33
+ assert_equal('reason', handler.reason)
34
+ end
35
+
36
+ it "fails if the given task is not available" do
37
+ assert_raises(HexaPDF::Error) { @doc.signatures.signing_handler(name: :unknown) }
38
+ end
39
+ end
40
+
41
+ describe "add" do
42
+ before do
43
+ @doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
44
+ @io = StringIO.new(''.b)
45
+ @handler = @doc.signatures.signing_handler(
46
+ certificate: CERTIFICATES.signer_certificate,
47
+ key: CERTIFICATES.signer_key,
48
+ certificate_chain: [CERTIFICATES.ca_certificate]
49
+ )
50
+ end
51
+
52
+ it "uses the provided signature dictionary" do
53
+ sig = @doc.add({Type: :Sig, Key: :value})
54
+ @doc.signatures.add(@io, @handler, signature: sig)
55
+ assert_equal(1, @doc.signatures.to_a.compact.size)
56
+ assert_equal(:value, @doc.signatures.to_a[0][:Key])
57
+ refute_equal(:value, @doc.acro_form.each_field.first[:Key])
58
+ end
59
+
60
+ it "creates the signature dictionary if none is provided" do
61
+ @doc.signatures.add(@io, @handler)
62
+ assert_equal(1, @doc.signatures.to_a.compact.size)
63
+ refute(@doc.acro_form.each_field.first.key?(:Contents))
64
+ end
65
+
66
+ it "sets the needed information on the signature dictionary" do
67
+ def @handler.finalize_objects(sigfield, sig)
68
+ sig[:key] = :sig
69
+ sigfield[:key] = :sig_field
70
+ end
71
+ @doc.signatures.add(@io, @handler, write_options: {update_fields: false})
72
+ sig = @doc.signatures.first
73
+ assert_equal([0, 925, 925 + sig[:Contents].size * 2 + 2, 2501], sig[:ByteRange].value)
74
+ assert_equal(:sig, sig[:key])
75
+ assert_equal(:sig_field, @doc.acro_form.each_field.first[:key])
76
+ assert(sig.key?(:Contents))
77
+ end
78
+
79
+ it "creates the main form dictionary if necessary" do
80
+ @doc.signatures.add(@io, @handler)
81
+ assert(@doc.acro_form)
82
+ assert_equal([:signatures_exist, :append_only], @doc.acro_form.signature_flags)
83
+ end
84
+
85
+ it "uses the provided signature field" do
86
+ field = @doc.acro_form(create: true).create_signature_field('Signature2')
87
+ @doc.signatures.add(@io, @handler, signature: field)
88
+ assert_nil(@doc.acro_form.field_by_name("Signature3"))
89
+ refute_nil(field.field_value)
90
+ assert_nil(@doc.signatures.first[:T])
91
+ end
92
+
93
+ it "uses an existing signature field if possible" do
94
+ field = @doc.acro_form(create: true).create_signature_field('Signature2')
95
+ field.field_value = sig = @doc.add({Type: :Sig, key: :value})
96
+ @doc.signatures.add(@io, @handler, signature: sig)
97
+ assert_nil(@doc.acro_form.field_by_name("Signature3"))
98
+ assert_same(sig, @doc.signatures.first)
99
+ end
100
+
101
+ it "creates the signature field if necessary" do
102
+ @doc.acro_form(create: true).create_text_field('Signature2')
103
+ @doc.signatures.add(@io, @handler)
104
+ field = @doc.acro_form.field_by_name("Signature3")
105
+ assert_equal(:Sig, field.field_type)
106
+ refute_nil(field.field_value)
107
+ assert_equal(1, field.each_widget.count)
108
+ end
109
+
110
+ it "handles different xref section types correctly when determing the offsets" do
111
+ @doc.delete(7)
112
+ sig = @doc.signatures.add(@io, @handler, write_options: {update_fields: false})
113
+ assert_equal([0, 1036, 1036 + sig[:Contents].size * 2 + 2, 2483], sig[:ByteRange].value)
114
+ end
115
+
116
+ it "works if the signature object is the last object of the xref section" do
117
+ field = @doc.acro_form(create: true).create_signature_field('Signature2')
118
+ field.create_widget(@doc.pages[0], Rect: [0, 0, 0, 0])
119
+ sig = @doc.signatures.add(@io, @handler, signature: field, write_options: {update_fields: false})
120
+ assert_equal([0, 3143, 3143 + sig[:Contents].size * 2 + 2, 380], sig[:ByteRange].value)
121
+ end
122
+
123
+ it "allows writing to a file in addition to writing to an IO" do
124
+ tempfile = Tempfile.new('hexapdf-signature')
125
+ tempfile.close
126
+ @doc.signatures.add(tempfile.path, @handler)
127
+ doc = HexaPDF::Document.open(tempfile.path)
128
+ assert(doc.signatures.first.verify(allow_self_signed: true).success?)
129
+ end
130
+
131
+ it "adds a new revision with the signature" do
132
+ @doc.signatures.add(@io, @handler)
133
+ signed_doc = HexaPDF::Document.new(io: @io)
134
+ assert(signed_doc.signatures.first.verify)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require 'hexapdf/digital_signature'
6
+ require_relative 'common'
7
+
8
+ describe HexaPDF::DigitalSignature::Signing do
9
+ before do
10
+ @handler = HexaPDF::DigitalSignature::Signing::DefaultHandler.new(
11
+ certificate: CERTIFICATES.signer_certificate,
12
+ key: CERTIFICATES.signer_key,
13
+ certificate_chain: [CERTIFICATES.ca_certificate]
14
+ )
15
+ end
16
+
17
+ it "allows embedding an external signature value" do
18
+ # Create first signature normally for testing the signature-finding code later
19
+ doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
20
+ io = StringIO.new(''.b)
21
+ doc.signatures.add(io, @handler)
22
+ doc = HexaPDF::Document.new(io: io)
23
+ io = StringIO.new(''.b)
24
+
25
+ byte_range = nil
26
+ @handler.signature_size = 5000
27
+ @handler.certificate = nil
28
+ @handler.external_signing = proc {|_, br| byte_range = br; "" }
29
+ doc.signatures.add(io, @handler)
30
+
31
+ io.pos = byte_range[0]
32
+ data = io.read(byte_range[1])
33
+ io.pos = byte_range[2]
34
+ data << io.read(byte_range[3])
35
+ contents = OpenSSL::PKCS7.sign(CERTIFICATES.signer_certificate, @handler.key, data,
36
+ @handler.certificate_chain,
37
+ OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY).to_der
38
+ HexaPDF::DigitalSignature::Signing.embed_signature(io, contents)
39
+ doc = HexaPDF::Document.new(io: io)
40
+ assert_equal(2, doc.signatures.each.count)
41
+ doc.signatures.each do |signature|
42
+ assert(signature.verify(allow_self_signed: true).messages.find {|m| m.content == 'Signature valid' })
43
+ end
44
+ end
45
+
46
+ it "fails if the reserved signature space is too small" do
47
+ doc = HexaPDF::Document.new(io: StringIO.new(MINIMAL_PDF))
48
+ io = StringIO.new(''.b)
49
+ def @handler.signature_size; 200; end
50
+ msg = assert_raises(HexaPDF::Error) { doc.signatures.add(io, @handler) }
51
+ assert_match(/space.*too small.*200 vs/, msg.message)
52
+ end
53
+ end
@@ -1,26 +1,26 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
 
3
3
  require 'test_helper'
4
- require 'hexapdf/type/signature'
4
+ require 'hexapdf/digital_signature'
5
5
 
6
- describe HexaPDF::Type::Signature::VerificationResult do
6
+ describe HexaPDF::DigitalSignature::VerificationResult do
7
7
  describe "Message" do
8
8
  it "accepts a type and a content argument on creation" do
9
- m = HexaPDF::Type::Signature::VerificationResult::Message.new(:type, 'content')
9
+ m = HexaPDF::DigitalSignature::VerificationResult::Message.new(:type, 'content')
10
10
  assert_equal(:type, m.type)
11
11
  assert_equal('content', m.content)
12
12
  end
13
13
 
14
14
  it "allows sorting by type" do
15
- info = HexaPDF::Type::Signature::VerificationResult::Message.new(:info, 'c')
16
- warning = HexaPDF::Type::Signature::VerificationResult::Message.new(:warning, 'c')
17
- error = HexaPDF::Type::Signature::VerificationResult::Message.new(:error, 'c')
15
+ info = HexaPDF::DigitalSignature::VerificationResult::Message.new(:info, 'c')
16
+ warning = HexaPDF::DigitalSignature::VerificationResult::Message.new(:warning, 'c')
17
+ error = HexaPDF::DigitalSignature::VerificationResult::Message.new(:error, 'c')
18
18
  assert_equal([error, warning, info], [info, error, warning].sort)
19
19
  end
20
20
  end
21
21
 
22
22
  before do
23
- @result = HexaPDF::Type::Signature::VerificationResult.new
23
+ @result = HexaPDF::DigitalSignature::VerificationResult.new
24
24
  end
25
25
 
26
26
  it "can add new messages" do
@@ -175,7 +175,6 @@ describe HexaPDF::DictionaryFields do
175
175
 
176
176
  it "allows conversion to a Time object from a binary string" do
177
177
  refute(@field.convert('test'.b, self))
178
- refute(@field.convert('D:01211016165909+00\'64'.b, self))
179
178
 
180
179
  [
181
180
  ["D:1998", [1998, 01, 01, 00, 00, 00, "-00:00"]],
@@ -191,6 +190,8 @@ describe HexaPDF::DictionaryFields do
191
190
  ["D:1998-08'00'", [1998, 01, 01, 00, 00, 00, "-08:00"]],
192
191
  ["D:19981223195210-08", [1998, 12, 23, 19, 52, 10, "-08:00"]], # non-standard, missing '
193
192
  ["D:19981223195210-08'00", [1998, 12, 23, 19, 52, 10, "-08:00"]], # non-standard, missing '
193
+ ["D:19981223195210-54'00", [1998, 12, 23, 19, 52, 10, "-23:59:59"]], # non-standard, TZ hour to large
194
+ ["D:19981223195210+10'65", [1998, 12, 23, 19, 52, 10, "+11:05"]], # non-standard, TZ min to large
194
195
  ].each do |str, data|
195
196
  obj = @field.convert(str, self)
196
197
  assert_equal(Time.new(*data), obj, "date str used: #{str}")
@@ -504,7 +504,7 @@ describe HexaPDF::Document do
504
504
 
505
505
  it "allows to conveniently sign a document" do
506
506
  mock = Minitest::Mock.new
507
- mock.expect(:handler, :handler, name: :handler, opt: :key)
507
+ mock.expect(:signing_handler, :handler, name: :handler, opt: :key)
508
508
  mock.expect(:add, :added, [:io, :handler], signature: :sig, write_options: :write_options)
509
509
  @doc.instance_variable_set(:@signatures, mock)
510
510
  result = @doc.sign(:io, handler: :handler, write_options: :write_options, signature: :sig, opt: :key)
@@ -40,7 +40,7 @@ describe HexaPDF::Writer do
40
40
  219
41
41
  %%EOF
42
42
  3 0 obj
43
- <</Producer(HexaPDF version 0.28.0)>>
43
+ <</Producer(HexaPDF version 0.29.0)>>
44
44
  endobj
45
45
  xref
46
46
  3 1
@@ -72,7 +72,7 @@ describe HexaPDF::Writer do
72
72
  141
73
73
  %%EOF
74
74
  6 0 obj
75
- <</Producer(HexaPDF version 0.28.0)>>
75
+ <</Producer(HexaPDF version 0.29.0)>>
76
76
  endobj
77
77
  2 0 obj
78
78
  <</Length 10>>stream
@@ -214,7 +214,7 @@ describe HexaPDF::Writer do
214
214
  <</Type/Page/MediaBox[0 0 595 842]/Parent 2 0 R/Resources<<>>>>
215
215
  endobj
216
216
  5 0 obj
217
- <</Producer(HexaPDF version 0.28.0)>>
217
+ <</Producer(HexaPDF version 0.29.0)>>
218
218
  endobj
219
219
  4 0 obj
220
220
  <</Root 1 0 R/Info 5 0 R/Size 6/Type/XRef/W[1 1 2]/Index[0 6]/Filter/FlateDecode/DecodeParms<</Columns 4/Predictor 12>>/Length 33>>stream
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hexapdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.28.0
4
+ version: 0.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomas Leitner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-12-30 00:00:00.000000000 Z
11
+ date: 2023-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cmdparse
@@ -291,6 +291,7 @@ files:
291
291
  - examples/021-list_box.rb
292
292
  - examples/022-outline.rb
293
293
  - examples/023-images.rb
294
+ - examples/024-digital-signatures.rb
294
295
  - examples/emoji-smile.png
295
296
  - examples/emoji-wink.png
296
297
  - examples/machupicchu.jpg
@@ -328,6 +329,17 @@ files:
328
329
  - lib/hexapdf/data_dir.rb
329
330
  - lib/hexapdf/dictionary.rb
330
331
  - lib/hexapdf/dictionary_fields.rb
332
+ - lib/hexapdf/digital_signature.rb
333
+ - lib/hexapdf/digital_signature/cms_handler.rb
334
+ - lib/hexapdf/digital_signature/handler.rb
335
+ - lib/hexapdf/digital_signature/pkcs1_handler.rb
336
+ - lib/hexapdf/digital_signature/signature.rb
337
+ - lib/hexapdf/digital_signature/signatures.rb
338
+ - lib/hexapdf/digital_signature/signing.rb
339
+ - lib/hexapdf/digital_signature/signing/default_handler.rb
340
+ - lib/hexapdf/digital_signature/signing/signed_data_creator.rb
341
+ - lib/hexapdf/digital_signature/signing/timestamp_handler.rb
342
+ - lib/hexapdf/digital_signature/verification_result.rb
331
343
  - lib/hexapdf/document.rb
332
344
  - lib/hexapdf/document/destinations.rb
333
345
  - lib/hexapdf/document/files.rb
@@ -335,7 +347,6 @@ files:
335
347
  - lib/hexapdf/document/images.rb
336
348
  - lib/hexapdf/document/layout.rb
337
349
  - lib/hexapdf/document/pages.rb
338
- - lib/hexapdf/document/signatures.rb
339
350
  - lib/hexapdf/encryption.rb
340
351
  - lib/hexapdf/encryption/aes.rb
341
352
  - lib/hexapdf/encryption/arc4.rb
@@ -486,11 +497,6 @@ files:
486
497
  - lib/hexapdf/type/page_label.rb
487
498
  - lib/hexapdf/type/page_tree_node.rb
488
499
  - lib/hexapdf/type/resources.rb
489
- - lib/hexapdf/type/signature.rb
490
- - lib/hexapdf/type/signature/adbe_pkcs7_detached.rb
491
- - lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb
492
- - lib/hexapdf/type/signature/handler.rb
493
- - lib/hexapdf/type/signature/verification_result.rb
494
500
  - lib/hexapdf/type/trailer.rb
495
501
  - lib/hexapdf/type/viewer_preferences.rb
496
502
  - lib/hexapdf/type/xref_stream.rb
@@ -587,13 +593,23 @@ files:
587
593
  - test/hexapdf/content/test_parser.rb
588
594
  - test/hexapdf/content/test_processor.rb
589
595
  - test/hexapdf/content/test_transformation_matrix.rb
596
+ - test/hexapdf/digital_signature/common.rb
597
+ - test/hexapdf/digital_signature/signing/test_default_handler.rb
598
+ - test/hexapdf/digital_signature/signing/test_signed_data_creator.rb
599
+ - test/hexapdf/digital_signature/signing/test_timestamp_handler.rb
600
+ - test/hexapdf/digital_signature/test_cms_handler.rb
601
+ - test/hexapdf/digital_signature/test_handler.rb
602
+ - test/hexapdf/digital_signature/test_pkcs1_handler.rb
603
+ - test/hexapdf/digital_signature/test_signature.rb
604
+ - test/hexapdf/digital_signature/test_signatures.rb
605
+ - test/hexapdf/digital_signature/test_signing.rb
606
+ - test/hexapdf/digital_signature/test_verification_result.rb
590
607
  - test/hexapdf/document/test_destinations.rb
591
608
  - test/hexapdf/document/test_files.rb
592
609
  - test/hexapdf/document/test_fonts.rb
593
610
  - test/hexapdf/document/test_images.rb
594
611
  - test/hexapdf/document/test_layout.rb
595
612
  - test/hexapdf/document/test_pages.rb
596
- - test/hexapdf/document/test_signatures.rb
597
613
  - test/hexapdf/encryption/common.rb
598
614
  - test/hexapdf/encryption/test_aes.rb
599
615
  - test/hexapdf/encryption/test_arc4.rb
@@ -705,11 +721,6 @@ files:
705
721
  - test/hexapdf/type/annotations/test_markup_annotation.rb
706
722
  - test/hexapdf/type/annotations/test_text.rb
707
723
  - test/hexapdf/type/annotations/test_widget.rb
708
- - test/hexapdf/type/signature/common.rb
709
- - test/hexapdf/type/signature/test_adbe_pkcs7_detached.rb
710
- - test/hexapdf/type/signature/test_adbe_x509_rsa_sha1.rb
711
- - test/hexapdf/type/signature/test_handler.rb
712
- - test/hexapdf/type/signature/test_verification_result.rb
713
724
  - test/hexapdf/type/test_annotation.rb
714
725
  - test/hexapdf/type/test_catalog.rb
715
726
  - test/hexapdf/type/test_cid_font.rb
@@ -732,7 +743,6 @@ files:
732
743
  - test/hexapdf/type/test_page_label.rb
733
744
  - test/hexapdf/type/test_page_tree_node.rb
734
745
  - test/hexapdf/type/test_resources.rb
735
- - test/hexapdf/type/test_signature.rb
736
746
  - test/hexapdf/type/test_trailer.rb
737
747
  - test/hexapdf/type/test_xref_stream.rb
738
748
  - test/hexapdf/utils/test_bit_field.rb