hexapdf 0.27.0 → 0.29.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -11
  3. data/examples/019-acro_form.rb +14 -3
  4. data/examples/023-images.rb +30 -0
  5. data/examples/024-digital-signatures.rb +23 -0
  6. data/lib/hexapdf/cli/info.rb +5 -1
  7. data/lib/hexapdf/cli/inspect.rb +2 -2
  8. data/lib/hexapdf/cli/split.rb +2 -2
  9. data/lib/hexapdf/configuration.rb +13 -14
  10. data/lib/hexapdf/content/canvas.rb +8 -3
  11. data/lib/hexapdf/dictionary.rb +1 -5
  12. data/lib/hexapdf/dictionary_fields.rb +6 -2
  13. data/lib/hexapdf/digital_signature/cms_handler.rb +137 -0
  14. data/lib/hexapdf/digital_signature/handler.rb +138 -0
  15. data/lib/hexapdf/digital_signature/pkcs1_handler.rb +96 -0
  16. data/lib/hexapdf/{type → digital_signature}/signature.rb +3 -8
  17. data/lib/hexapdf/digital_signature/signatures.rb +210 -0
  18. data/lib/hexapdf/digital_signature/signing/default_handler.rb +317 -0
  19. data/lib/hexapdf/digital_signature/signing/signed_data_creator.rb +308 -0
  20. data/lib/hexapdf/digital_signature/signing/timestamp_handler.rb +148 -0
  21. data/lib/hexapdf/digital_signature/signing.rb +101 -0
  22. data/lib/hexapdf/{type/signature → digital_signature}/verification_result.rb +37 -41
  23. data/lib/hexapdf/digital_signature.rb +56 -0
  24. data/lib/hexapdf/document.rb +27 -24
  25. data/lib/hexapdf/encryption/standard_security_handler.rb +2 -1
  26. data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
  27. data/lib/hexapdf/importer.rb +32 -27
  28. data/lib/hexapdf/layout/list_box.rb +1 -5
  29. data/lib/hexapdf/object.rb +5 -0
  30. data/lib/hexapdf/parser.rb +13 -0
  31. data/lib/hexapdf/revision.rb +15 -12
  32. data/lib/hexapdf/revisions.rb +4 -0
  33. data/lib/hexapdf/tokenizer.rb +14 -8
  34. data/lib/hexapdf/type/acro_form/appearance_generator.rb +174 -128
  35. data/lib/hexapdf/type/acro_form/button_field.rb +5 -3
  36. data/lib/hexapdf/type/acro_form/choice_field.rb +2 -0
  37. data/lib/hexapdf/type/acro_form/field.rb +11 -5
  38. data/lib/hexapdf/type/acro_form/form.rb +33 -7
  39. data/lib/hexapdf/type/acro_form/signature_field.rb +2 -0
  40. data/lib/hexapdf/type/acro_form/text_field.rb +12 -2
  41. data/lib/hexapdf/type/annotations/widget.rb +3 -0
  42. data/lib/hexapdf/type/font_true_type.rb +14 -0
  43. data/lib/hexapdf/type/object_stream.rb +2 -2
  44. data/lib/hexapdf/type/outline.rb +1 -1
  45. data/lib/hexapdf/type/page.rb +56 -46
  46. data/lib/hexapdf/type.rb +0 -1
  47. data/lib/hexapdf/version.rb +1 -1
  48. data/lib/hexapdf/writer.rb +2 -3
  49. data/test/hexapdf/content/test_canvas.rb +5 -0
  50. data/test/hexapdf/{type/signature → digital_signature}/common.rb +34 -4
  51. data/test/hexapdf/digital_signature/signing/test_default_handler.rb +162 -0
  52. data/test/hexapdf/digital_signature/signing/test_signed_data_creator.rb +225 -0
  53. data/test/hexapdf/digital_signature/signing/test_timestamp_handler.rb +88 -0
  54. data/test/hexapdf/{type/signature/test_adbe_pkcs7_detached.rb → digital_signature/test_cms_handler.rb} +7 -7
  55. data/test/hexapdf/{type/signature → digital_signature}/test_handler.rb +4 -4
  56. data/test/hexapdf/{type/signature/test_adbe_x509_rsa_sha1.rb → digital_signature/test_pkcs1_handler.rb} +3 -3
  57. data/test/hexapdf/{type → digital_signature}/test_signature.rb +7 -7
  58. data/test/hexapdf/digital_signature/test_signatures.rb +137 -0
  59. data/test/hexapdf/digital_signature/test_signing.rb +53 -0
  60. data/test/hexapdf/{type/signature → digital_signature}/test_verification_result.rb +7 -7
  61. data/test/hexapdf/document/test_pages.rb +2 -2
  62. data/test/hexapdf/encryption/test_aes.rb +1 -1
  63. data/test/hexapdf/filter/test_predictor.rb +0 -1
  64. data/test/hexapdf/layout/test_box.rb +2 -1
  65. data/test/hexapdf/layout/test_column_box.rb +1 -1
  66. data/test/hexapdf/layout/test_list_box.rb +1 -1
  67. data/test/hexapdf/test_dictionary_fields.rb +2 -1
  68. data/test/hexapdf/test_document.rb +3 -9
  69. data/test/hexapdf/test_importer.rb +13 -6
  70. data/test/hexapdf/test_parser.rb +17 -0
  71. data/test/hexapdf/test_revision.rb +15 -14
  72. data/test/hexapdf/test_revisions.rb +43 -0
  73. data/test/hexapdf/test_stream.rb +1 -1
  74. data/test/hexapdf/test_tokenizer.rb +3 -4
  75. data/test/hexapdf/test_writer.rb +3 -3
  76. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +135 -56
  77. data/test/hexapdf/type/acro_form/test_button_field.rb +6 -1
  78. data/test/hexapdf/type/acro_form/test_choice_field.rb +4 -0
  79. data/test/hexapdf/type/acro_form/test_field.rb +4 -4
  80. data/test/hexapdf/type/acro_form/test_form.rb +18 -0
  81. data/test/hexapdf/type/acro_form/test_signature_field.rb +4 -0
  82. data/test/hexapdf/type/acro_form/test_text_field.rb +13 -0
  83. data/test/hexapdf/type/test_font_true_type.rb +20 -0
  84. data/test/hexapdf/type/test_object_stream.rb +2 -1
  85. data/test/hexapdf/type/test_outline.rb +3 -0
  86. data/test/hexapdf/type/test_page.rb +67 -30
  87. data/test/hexapdf/type/test_page_tree_node.rb +4 -2
  88. metadata +69 -16
  89. data/lib/hexapdf/document/signatures.rb +0 -546
  90. data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +0 -135
  91. data/lib/hexapdf/type/signature/adbe_x509_rsa_sha1.rb +0 -95
  92. data/lib/hexapdf/type/signature/handler.rb +0 -140
  93. data/test/hexapdf/document/test_signatures.rb +0 -352
@@ -0,0 +1,162 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require_relative '../common'
6
+
7
+ describe HexaPDF::DigitalSignature::Signing::DefaultHandler do
8
+ before do
9
+ @doc = HexaPDF::Document.new
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 "defaults to standard CMS signatures" do
18
+ assert_equal(:cms, @handler.signature_type)
19
+ end
20
+
21
+ it "returns the size of serialized signature" do
22
+ assert(@handler.signature_size > 1000)
23
+ @handler.signature_size = 100
24
+ assert_equal(100, @handler.signature_size)
25
+ end
26
+
27
+ it "allows setting the DocMDP permissions" do
28
+ assert_nil(@handler.doc_mdp_permissions)
29
+
30
+ @handler.doc_mdp_permissions = :no_changes
31
+ assert_equal(1, @handler.doc_mdp_permissions)
32
+ @handler.doc_mdp_permissions = 1
33
+ assert_equal(1, @handler.doc_mdp_permissions)
34
+
35
+ @handler.doc_mdp_permissions = :form_filling
36
+ assert_equal(2, @handler.doc_mdp_permissions)
37
+ @handler.doc_mdp_permissions = 2
38
+ assert_equal(2, @handler.doc_mdp_permissions)
39
+
40
+ @handler.doc_mdp_permissions = :form_filling_and_annotations
41
+ assert_equal(3, @handler.doc_mdp_permissions)
42
+ @handler.doc_mdp_permissions = 3
43
+ assert_equal(3, @handler.doc_mdp_permissions)
44
+
45
+ @handler.doc_mdp_permissions = nil
46
+ assert_nil(@handler.doc_mdp_permissions)
47
+
48
+ assert_raises(ArgumentError) { @handler.doc_mdp_permissions = :other }
49
+ end
50
+
51
+ describe "sign" do
52
+ it "can sign the data using the provided certificate and key" do
53
+ data = StringIO.new("data")
54
+ signed_data = @handler.sign(data, [0, data.string.size, 0, 0])
55
+
56
+ pkcs7 = OpenSSL::PKCS7.new(signed_data)
57
+ assert(pkcs7.detached?)
58
+ assert_equal([CERTIFICATES.signer_certificate, CERTIFICATES.ca_certificate],
59
+ pkcs7.certificates)
60
+ store = OpenSSL::X509::Store.new
61
+ store.add_cert(CERTIFICATES.ca_certificate)
62
+ assert(pkcs7.verify([], store, data.string, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY))
63
+ end
64
+
65
+ it "can change the used digest algorithm" do
66
+ @handler.digest_algorithm = 'sha384'
67
+ asn1 = OpenSSL::ASN1.decode(@handler.sign(StringIO.new('data'), [0, 4, 0, 0]))
68
+ assert_equal('SHA384', asn1.value[1].value[0].value[1].value[0].value[0].value)
69
+ end
70
+
71
+ it "can embed a timestamp token" do
72
+ @handler.timestamp_handler = tsh = Object.new
73
+ tsh.define_singleton_method(:sign) {|_, _| OpenSSL::ASN1::OctetString.new("signed-tsh") }
74
+ signed = @handler.sign(StringIO.new('data'), [0, 4, 0, 0])
75
+ asn1 = OpenSSL::ASN1.decode(signed)
76
+ assert_equal('signed-tsh', asn1.value[1].value[0].value[4].value[0].
77
+ value[6].value[0].value[1].value[0].value)
78
+ end
79
+
80
+ it "creates PAdES compatible signatures" do
81
+ @handler.signature_type = :pades
82
+ signed = @handler.sign(StringIO.new('data'), [0, 4, 0, 0])
83
+ asn1 = OpenSSL::ASN1.decode(signed)
84
+ # check by absence of signing-time signed attribute
85
+ refute(asn1.value[1].value[0].value[4].value[0].value[3].value.
86
+ find {|obj| obj.value[0].value == 'signingTime' })
87
+ end
88
+
89
+ it "can use external signing without certificate set" do
90
+ @handler.certificate = nil
91
+ @handler.external_signing = proc { "hallo" }
92
+ assert_equal("hallo", @handler.sign(StringIO.new, [0, 0, 0, 0]))
93
+ end
94
+
95
+ it "can use external signing with certificate set but not the key" do
96
+ @handler.key = nil
97
+ @handler.external_signing = proc do |algorithm, _hash|
98
+ assert_equal('sha256', algorithm)
99
+ "hallo"
100
+ end
101
+ result = @handler.sign(StringIO.new, [0, 0, 0, 0])
102
+ asn1 = OpenSSL::ASN1.decode(result)
103
+ assert_equal("hallo", asn1.value[1].value[0].value[4].value[0].value[5].value)
104
+ end
105
+ end
106
+
107
+ describe "finalize_objects" do
108
+ before do
109
+ @field = @doc.wrap({})
110
+ @obj = @doc.wrap({})
111
+ end
112
+
113
+ it "only sets the mandatory values if no concrete finalization tasks need to be done" do
114
+ @handler.finalize_objects(@field, @obj)
115
+ assert(@field.empty?)
116
+ assert_equal(:'Adobe.PPKLite', @obj[:Filter])
117
+ assert_equal(:'adbe.pkcs7.detached', @obj[:SubFilter])
118
+ assert_kind_of(Time, @obj[:M])
119
+ end
120
+
121
+ it "adjust the /SubFilter if signature type is pades" do
122
+ @handler.signature_type = :pades
123
+ @handler.finalize_objects(@field, @obj)
124
+ assert_equal(:'ETSI.CAdES.detached', @obj[:SubFilter])
125
+ end
126
+
127
+ it "sets the reason, location and contact info fields" do
128
+ @handler.reason = 'Reason'
129
+ @handler.location = 'Location'
130
+ @handler.contact_info = 'Contact'
131
+ @handler.finalize_objects(@field, @obj)
132
+ assert(@field.empty?)
133
+ assert_equal(['Reason', 'Location', 'Contact'], @obj.value.values_at(:Reason, :Location, :ContactInfo))
134
+ end
135
+
136
+ it "fills the build properties dictionary with appropriate application information" do
137
+ @handler.finalize_objects(@field, @obj)
138
+ assert_equal(:HexaPDF, @obj[:Prop_Build][:App][:Name])
139
+ assert_equal(HexaPDF::VERSION, @obj[:Prop_Build][:App][:REx])
140
+ end
141
+
142
+ it "applies the specified DocMDP permissions" do
143
+ @handler.doc_mdp_permissions = :no_changes
144
+ @handler.finalize_objects(@field, @obj)
145
+ ref = @obj[:Reference][0]
146
+ assert_equal(:DocMDP, ref[:TransformMethod])
147
+ assert_equal(:SHA256, ref[:DigestMethod])
148
+ assert_equal(1, ref[:TransformParams][:P])
149
+ assert_equal(:'1.2', ref[:TransformParams][:V])
150
+ assert_same(@obj, @doc.catalog[:Perms][:DocMDP])
151
+ end
152
+
153
+ it "fails if DocMDP should be set but there is already a signature" do
154
+ @handler.doc_mdp_permissions = :no_changes
155
+ 2.times do
156
+ field = @doc.acro_form(create: true).create_signature_field('test')
157
+ field.field_value = :something
158
+ end
159
+ assert_raises(HexaPDF::Error) { @handler.finalize_objects(@field, @obj) }
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,225 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+ require 'hexapdf/document'
5
+ require_relative '../common'
6
+
7
+ describe HexaPDF::DigitalSignature::Signing::SignedDataCreator do
8
+ before do
9
+ @klass = HexaPDF::DigitalSignature::Signing::SignedDataCreator
10
+ @signed_data = @klass.new
11
+ @signed_data.certificate = CERTIFICATES.signer_certificate
12
+ @signed_data.key = CERTIFICATES.signer_key
13
+ @signed_data.certificates = [CERTIFICATES.ca_certificate]
14
+ end
15
+
16
+ it "allows setting the attributes" do
17
+ obj = @klass.new
18
+ obj.certificate = :cert
19
+ obj.key = :key
20
+ obj.certificates = :certs
21
+ obj.digest_algorithm = 'sha512'
22
+ obj.timestamp_handler = :tsh
23
+ assert_equal(:cert, obj.certificate)
24
+ assert_equal(:key, obj.key)
25
+ assert_equal(:certs, obj.certificates)
26
+ assert_equal('sha512', obj.digest_algorithm)
27
+ assert_equal(:tsh, obj.timestamp_handler)
28
+ end
29
+
30
+ it "doesn't allow setting attributes to nil using ::create" do
31
+ asn1 = @klass.create("data",
32
+ certificate: CERTIFICATES.signer_certificate,
33
+ key: CERTIFICATES.signer_key,
34
+ digest_algorithm: nil)
35
+ assert_equal('2.16.840.1.101.3.4.2.1', asn1.value[1].value[1].value[0].value[0].value)
36
+ end
37
+
38
+ describe "content info structure" do
39
+ it "sets the correct content type value for the outer container" do
40
+ asn1 = @signed_data.create("data")
41
+ assert_equal('1.2.840.113549.1.7.2', asn1.value[0].value)
42
+ end
43
+
44
+ it "has the signed data structure marked as explicit" do
45
+ asn1 = @signed_data.create("data")
46
+ signed_data = asn1.value[1]
47
+ assert_equal(0, signed_data.tag)
48
+ assert_equal(:EXPLICIT, signed_data.tagging)
49
+ assert_equal(:CONTEXT_SPECIFIC, signed_data.tag_class)
50
+ end
51
+ end
52
+
53
+ describe "signed data structure" do
54
+ before do
55
+ @structure = @signed_data.create("data").value[1]
56
+ end
57
+
58
+ it "sets the correct version" do
59
+ assert_equal(1, @structure.value[0].value)
60
+ end
61
+
62
+ it "contains a reference to the used digest algorithm" do
63
+ assert_equal('2.16.840.1.101.3.4.2.1', @structure.value[1].value[0].value[0].value)
64
+ assert_nil(@structure.value[1].value[0].value[1].value)
65
+ end
66
+
67
+ it "contains an empty encapsulated content structure" do
68
+ assert_equal(1, @structure.value[2].value.size)
69
+ assert_equal('1.2.840.113549.1.7.1', @structure.value[2].value[0].value)
70
+ end
71
+
72
+ it "contains the assigned certificates" do
73
+ assert_equal(2, @structure.value[3].value.size)
74
+ assert_equal(0, @structure.value[3].tag)
75
+ assert_equal(:IMPLICIT, @structure.value[3].tagging)
76
+ assert_equal(:CONTEXT_SPECIFIC, @structure.value[3].tag_class)
77
+ assert_equal([CERTIFICATES.signer_certificate, CERTIFICATES.ca_certificate],
78
+ @structure.value[3].value)
79
+ end
80
+
81
+ it "contains a single signer info structure" do
82
+ assert_equal(1, @structure.value[4].value.size)
83
+ end
84
+ end
85
+
86
+ describe "signer info" do
87
+ before do
88
+ @structure = @signed_data.create("data").value[1].value[4].value[0]
89
+ end
90
+
91
+ it "has the expected number of entries" do
92
+ assert_equal(6, @structure.value.size)
93
+ end
94
+
95
+ it "sets the correct version" do
96
+ assert_equal(1, @structure.value[0].value)
97
+ end
98
+
99
+ it "uses issuer and serial for the signer identifer" do
100
+ assert_equal(CERTIFICATES.signer_certificate.issuer, @structure.value[1].value[0])
101
+ assert_equal(CERTIFICATES.signer_certificate.serial, @structure.value[1].value[1].value)
102
+ end
103
+
104
+ it "contains a reference to the used digest algorithm" do
105
+ assert_equal('2.16.840.1.101.3.4.2.1', @structure.value[2].value[0].value)
106
+ assert_nil(@structure.value[2].value[1].value)
107
+ end
108
+
109
+ describe "signed attributes" do
110
+ it "uses the correct tagging for the attributes" do
111
+ assert_equal(0, @structure.value[3].tag)
112
+ assert_equal(:IMPLICIT, @structure.value[3].tagging)
113
+ assert_equal(:CONTEXT_SPECIFIC, @structure.value[3].tag_class)
114
+ end
115
+
116
+ it "contains the content type identifier" do
117
+ attr = @structure.value[3].value.find {|obj| obj.value[0].value == '1.2.840.113549.1.9.3' }
118
+ assert_equal('1.2.840.113549.1.7.1', attr.value[1].value[0].value)
119
+ end
120
+
121
+ it "contains the message digest attribute" do
122
+ attr = @structure.value[3].value.find {|obj| obj.value[0].value == '1.2.840.113549.1.9.4' }
123
+ assert_equal(OpenSSL::Digest.digest('SHA256', 'data'), attr.value[1].value[0].value)
124
+ end
125
+
126
+ it "contains the signing certificate attribute" do
127
+ attr = @structure.value[3].value.find {|obj| obj.value[0].value == '1.2.840.113549.1.9.16.2.47' }
128
+ signing_cert = attr.value[1].value[0]
129
+ assert_equal(1, signing_cert.value.size)
130
+ assert_equal(1, signing_cert.value[0].value.size)
131
+ assert_equal(2, signing_cert.value[0].value[0].value.size)
132
+ assert_equal(OpenSSL::Digest.digest('sha256', CERTIFICATES.signer_certificate.to_der),
133
+ signing_cert.value[0].value[0].value[0].value)
134
+ assert_equal(2, signing_cert.value[0].value[0].value[1].value.size)
135
+ assert_equal(1, signing_cert.value[0].value[0].value[1].value[0].value.size)
136
+ assert_equal(1, signing_cert.value[0].value[0].value[1].value[0].value[0].value.size)
137
+ assert_equal(4, signing_cert.value[0].value[0].value[1].value[0].value[0].tag)
138
+ assert_equal(:IMPLICIT, signing_cert.value[0].value[0].value[1].value[0].value[0].tagging)
139
+ assert_equal(:CONTEXT_SPECIFIC, signing_cert.value[0].value[0].value[1].value[0].value[0].tag_class)
140
+ assert_equal(CERTIFICATES.signer_certificate.issuer,
141
+ signing_cert.value[0].value[0].value[1].value[0].value[0].value[0])
142
+ assert_equal(CERTIFICATES.signer_certificate.serial,
143
+ signing_cert.value[0].value[0].value[1].value[1].value)
144
+ end
145
+ end
146
+
147
+ it "contains the signature algorithm reference" do
148
+ assert_equal('1.2.840.113549.1.1.1', @structure.value[4].value[0].value)
149
+ assert_nil(@structure.value[4].value[1].value)
150
+ end
151
+
152
+ it "contains the signature itself" do
153
+ to_sign = OpenSSL::ASN1::Set.new(@structure.value[3].value).to_der
154
+ assert_equal(CERTIFICATES.signer_key.sign('SHA256', to_sign), @structure.value[5].value)
155
+ end
156
+
157
+ it "fails if the signature algorithm is not supported" do
158
+ @signed_data.certificate = CERTIFICATES.dsa_signer_certificate
159
+ @signed_data.key = CERTIFICATES.dsa_signer_key
160
+ assert_raises(HexaPDF::Error) { @signed_data.create("data") }
161
+ end
162
+
163
+ it "can use a different digest algorithm" do
164
+ @signed_data.digest_algorithm = 'sha384'
165
+ structure = @signed_data.create("data").value[1].value[4].value[0]
166
+ to_sign = OpenSSL::ASN1::Set.new(structure.value[3].value).to_der
167
+ assert_equal('2.16.840.1.101.3.4.2.2', structure.value[2].value[0].value)
168
+ assert_equal(CERTIFICATES.signer_key.sign('SHA384', to_sign), structure.value[5].value)
169
+ end
170
+
171
+ it "allows delegating the signature to a provided signing block" do
172
+ @signed_data.key = nil
173
+ digest_algorithm = nil
174
+ calculated_hash = nil
175
+ structure = @signed_data.create("data") do |algorithm, hash|
176
+ digest_algorithm = algorithm
177
+ calculated_hash = hash
178
+ "signed"
179
+ end.value[1].value[4].value[0]
180
+ to_sign = OpenSSL::Digest.digest('SHA256', OpenSSL::ASN1::Set.new(structure.value[3].value).to_der)
181
+ assert_equal('sha256', digest_algorithm)
182
+ assert_equal(calculated_hash, to_sign)
183
+ assert_equal('signed', structure.value[5].value)
184
+ end
185
+
186
+ describe "unsigned attributes" do
187
+ it "allows adding a timestamp token" do
188
+ tsh = Object.new
189
+ io = nil
190
+ byte_range = nil
191
+ tsh.define_singleton_method(:sign) do |i_io, i_byte_range|
192
+ io = i_io
193
+ byte_range = i_byte_range
194
+ "timestamp"
195
+ end
196
+ @signed_data.timestamp_handler = tsh
197
+
198
+ structure = @signed_data.create("data").value[1].value[4].value[0]
199
+ assert_equal(structure.value[5].value, io.string)
200
+ assert_equal([0, io.string.size, 0, 0], byte_range)
201
+
202
+ attr = structure.value[6].value.find {|obj| obj.value[0].value == '1.2.840.113549.1.9.16.2.14' }
203
+ assert_equal("timestamp", attr.value[1].value[0])
204
+ end
205
+ end
206
+ end
207
+
208
+ describe "cms signature" do
209
+ it "includes the current time as signing time" do
210
+ Time.stub(:now, Time.at(0)) do
211
+ asn1 = OpenSSL::ASN1.decode(@signed_data.create("data"))
212
+ attr = asn1.value[1].value[0].value[4].value[0].value[3].value.
213
+ find {|obj| obj.value[0].value == 'signingTime' }
214
+ assert_equal(Time.now.utc, attr.value[1].value[0].value)
215
+ end
216
+ end
217
+ end
218
+
219
+ describe "pades signature" do
220
+ it "doesn't include the signing-time attribute" do
221
+ signer_info = @signed_data.create("data", type: :pades).value[1].value[4].value[0]
222
+ refute(signer_info.value[3].value.find {|obj| obj.value[0].value == 'signingTime' })
223
+ end
224
+ end
225
+ end
@@ -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