hexapdf 0.28.0 → 0.29.0

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