hanko_xml 0.1.0-aarch64-linux → 0.2.0-aarch64-linux

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.
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+
5
+ module HankoXml
6
+ module Xades
7
+ # The XAdES-LT validation material (#167): certificates for
8
+ # <xades:CertificateValues>, CRLs and OCSP responses for
9
+ # <xades:RevocationValues>. The caller collects it; the library never
10
+ # fetches. Nothing here is trusted: a verifier treats it as
11
+ # attacker-supplied until it chains to its own anchors (S-22, #168).
12
+ #
13
+ # Each entry is checked for its type only: X.509 certificates, X.509
14
+ # CRLs, and OCSPResponses whose responseStatus is successful.
15
+ ValidationData = Struct.new(:certificates, :crls, :ocsp_responses, keyword_init: true) do
16
+ # @param certificates [Array<OpenSSL::X509::Certificate, String, Key>] Anything Key.coerce reads.
17
+ # @param crls [Array<OpenSSL::X509::CRL, String>] PEM or DER.
18
+ # @param ocsp_responses [Array<OpenSSL::OCSP::Response, String>] DER OCSPResponse.
19
+ def initialize(certificates: [], crls: [], ocsp_responses: [])
20
+ super(
21
+ certificates: Array(certificates).map { |value| ValidationData.certificate(value) }.freeze,
22
+ crls: Array(crls).map { |value| ValidationData.crl(value) }.freeze,
23
+ ocsp_responses: Array(ocsp_responses).map { |value| ValidationData.ocsp_response(value) }.freeze
24
+ )
25
+ if to_h.values.all?(&:empty?)
26
+ raise HankoXml::ConfigurationError, "validation_data: needs certificates:, crls: or ocsp_responses:"
27
+ end
28
+
29
+ freeze
30
+ end
31
+
32
+ def self.coerce(value)
33
+ case value
34
+ when nil, ValidationData then value
35
+ when Hash then new(**value)
36
+ else
37
+ raise HankoXml::ConfigurationError,
38
+ "validation_data: must be a Xades::ValidationData, a Hash of its keywords or nil"
39
+ end
40
+ end
41
+
42
+ def self.certificate(value)
43
+ key = HankoXml::Key.coerce(value)
44
+ return key.to_openssl if key.certificate?
45
+
46
+ raise HankoXml::ConfigurationError, "validation_data certificates: must be X.509 certificates"
47
+ rescue HankoXml::KeyLoadError, ::ArgumentError
48
+ raise HankoXml::ConfigurationError, "validation_data certificates: must be X.509 certificates"
49
+ end
50
+
51
+ def self.crl(value)
52
+ return value if value.is_a?(OpenSSL::X509::CRL)
53
+ raise HankoXml::ConfigurationError, "validation_data crls: must be X.509 CRLs" unless value.is_a?(String)
54
+
55
+ OpenSSL::X509::CRL.new(value)
56
+ rescue OpenSSL::X509::CRLError
57
+ raise HankoXml::ConfigurationError, "validation_data crls: must be X.509 CRLs (PEM or DER)"
58
+ end
59
+
60
+ def self.ocsp_response(value)
61
+ response = value.is_a?(OpenSSL::OCSP::Response) ? value : parse_ocsp_response(value)
62
+ unless response.status == OpenSSL::OCSP::RESPONSE_STATUS_SUCCESSFUL && response.basic
63
+ raise HankoXml::ConfigurationError,
64
+ "validation_data ocsp_responses: must be successful OCSP responses " \
65
+ "(responseStatus #{response.status})"
66
+ end
67
+
68
+ response
69
+ end
70
+
71
+ def self.parse_ocsp_response(value)
72
+ unless value.is_a?(String)
73
+ raise HankoXml::ConfigurationError, "validation_data ocsp_responses: must be DER OCSPResponses"
74
+ end
75
+
76
+ OpenSSL::OCSP::Response.new(value)
77
+ rescue OpenSSL::OCSP::OCSPError
78
+ raise HankoXml::ConfigurationError, "validation_data ocsp_responses: must be DER OCSPResponses"
79
+ end
80
+ private_class_method :parse_ocsp_response
81
+
82
+ # Appends <xades:CertificateValues> and <xades:RevocationValues> (each
83
+ # only when it has entries) to +parent+, an UnsignedSignatureProperties
84
+ # in +namespace+.
85
+ def append_to(parent, namespace)
86
+ add = lambda do |node, name|
87
+ child = parent.document.create_element(name)
88
+ node.add_child(child)
89
+ child.namespace = namespace
90
+ child
91
+ end
92
+ encapsulate = ->(node, name, der) { add.call(node, name).content = [der].pack("m0") }
93
+
94
+ unless certificates.empty?
95
+ values = add.call(parent, "CertificateValues")
96
+ certificates.each do |certificate|
97
+ encapsulate.call(values, "EncapsulatedX509Certificate", certificate.to_der)
98
+ end
99
+ end
100
+ return if crls.empty? && ocsp_responses.empty?
101
+
102
+ # RevocationValuesType is a sequence: CRLValues, then OCSPValues.
103
+ revocation = add.call(parent, "RevocationValues")
104
+ unless crls.empty?
105
+ values = add.call(revocation, "CRLValues")
106
+ crls.each { |crl| encapsulate.call(values, "EncapsulatedCRLValue", crl.to_der) }
107
+ end
108
+ return if ocsp_responses.empty?
109
+
110
+ values = add.call(revocation, "OCSPValues")
111
+ ocsp_responses.each { |response| encapsulate.call(values, "EncapsulatedOCSPValue", response.to_der) }
112
+ end
113
+ end
114
+ end
115
+ end
@@ -11,11 +11,30 @@ module HankoXml
11
11
  class Verification < HankoXml::Verification
12
12
  # @return [SignedProperties, nil] Only when #valid?.
13
13
  attr_reader :signed_properties
14
-
15
- def initialize(signed_properties: nil, **attributes)
14
+ # @return [Array<SignatureTimeStamp>, nil] The verified SignatureTimeStamps (#163), in document order;
15
+ # empty when there are none, nil unless #valid? or with timestamps: :ignore.
16
+ attr_reader :signature_timestamps
17
+ # @return [LongTermValidation, nil] The XAdES-LT validation (#168): only when #valid? and the signature
18
+ # carries CertificateValues or RevocationValues that were checked.
19
+ attr_reader :long_term_validation
20
+ # @return [Array<SignatureTimeStamp>, nil] The verified xades141:ArchiveTimeStamps (#169), in document
21
+ # order; empty when there are none, nil unless #valid? or with timestamps: :ignore.
22
+ attr_reader :archive_timestamps
23
+
24
+ def initialize(signed_properties: nil, signature_timestamps: nil, long_term_validation: nil,
25
+ archive_timestamps: nil, **attributes)
16
26
  @signed_properties = signed_properties
27
+ @signature_timestamps = signature_timestamps&.freeze
28
+ @long_term_validation = long_term_validation
29
+ @archive_timestamps = archive_timestamps&.freeze
17
30
  super(**attributes)
18
31
  end
32
+
33
+ # The earliest verified SignatureTimeStamp time: when a trusted third
34
+ # party saw this signature at the latest. Nil without one.
35
+ def timestamp_time
36
+ signature_timestamps&.map(&:time)&.min
37
+ end
19
38
  end
20
39
 
21
40
  # Verifies a XAdES-B signature (#58): the XML-DSig signature through
@@ -31,14 +50,44 @@ module HankoXml
31
50
  # Reads XAdES 1.3.2 and the older 1.2.2 and 1.1.1 namespaces (#86); the
32
51
  # whole signature must use one.
33
52
  #
53
+ # XAdES-T (#163): every <xades:SignatureTimeStamp> in the signature's
54
+ # UnsignedSignatureProperties must verify against the TSA trust
55
+ # (tsa_store: / tsa_certificates:), kept apart from the signer's. A
56
+ # timestamp that does not verify fails the signature (fail closed).
57
+ # Xades::Locator finds QualifyingProperties and its unsigned containers.
58
+ #
59
+ # XAdES-LT (#168): when the signature embeds CertificateValues or
60
+ # RevocationValues, the signing certificate is validated at the time of
61
+ # the earliest verified SignatureTimeStamp: XML-DSig trust (chain,
62
+ # validity dates) at that time, then revocation evidence for every
63
+ # certificate below the trust anchor from the embedded CRLs and OCSP
64
+ # responses (Xades::LongTermCheck). A certificate that has expired since
65
+ # still verifies; embedded data that does not hold fails (fail closed).
66
+ # XAdES-LTA (#169): every xades141:ArchiveTimeStamp must verify too, over
67
+ # the EN 319 132-1 input (Xades::ArchiveTimeStampCheck), against the same
68
+ # TSA trust; fail closed, and not read with timestamps: :ignore.
69
+ #
34
70
  # Needs certificate-based trust (certificate:, certificates:, store:), and
35
71
  # Nokogiri: the signed properties are read from the DOM (#102).
36
- # Unsigned properties (XAdES-T/LT/LTA) are ignored: #81, #82.
37
72
  class Verifier < HankoXml::Verifier # rubocop:disable Metrics/ClassLength
73
+ TIMESTAMP_MODES = %i[verify require ignore].freeze
74
+ LONG_TERM_MODES = %i[verify require ignore].freeze
75
+
38
76
  # @param signature_policy [SignaturePolicy, Hash, :implied, nil] When given, the signed
39
77
  # SignaturePolicyIdentifier must be exactly this.
78
+ # @param timestamps [Symbol] :verify (default) checks every SignatureTimeStamp present; :require also
79
+ # fails a signature without one; :ignore does not read them, the XAdES-B behavior.
80
+ # @param tsa_store [OpenSSL::X509::Store, nil] Trust for the TSAs, not the signer.
81
+ # @param tsa_certificates [Array<OpenSSL::X509::Certificate, String, Key>, nil] Trusted TSA roots or
82
+ # certificates, instead of tsa_store:.
83
+ # @param long_term [Symbol, nil] :verify checks embedded XAdES-LT values when present; :require also
84
+ # fails a signature without them; :ignore does not read them. nil (default) is :verify, or :ignore with
85
+ # timestamps: :ignore, since the validation time comes from a SignatureTimeStamp.
86
+ # @param revocation_grace [Numeric] Seconds an embedded CRL or OCSP response may predate the validation
87
+ # time and still count as fresh. 0 (default): it must be issued at or after it.
40
88
  # All other keywords are {HankoXml::Verifier}'s.
41
- def initialize(signature_policy: nil, **options)
89
+ def initialize(signature_policy: nil, timestamps: :verify, tsa_store: nil, tsa_certificates: nil,
90
+ long_term: nil, revocation_grace: 0, **options)
42
91
  Dom.require!("HankoXml::Xades::Verifier")
43
92
  if options[:key] || options[:keys]
44
93
  raise HankoXml::ConfigurationError,
@@ -47,6 +96,12 @@ module HankoXml
47
96
  end
48
97
 
49
98
  @expected_signature_policy = coerce_signature_policy(signature_policy)
99
+ @timestamps = coerce_timestamps(timestamps)
100
+ @tsa_store = coerce_tsa_store(tsa_store, tsa_certificates)
101
+ @long_term = coerce_long_term(long_term)
102
+ @revocation_grace = coerce_revocation_grace(revocation_grace)
103
+ # Kept to verify the XML-DSig signature again at the validation time.
104
+ @options = options.freeze
50
105
  super(**options)
51
106
  end
52
107
 
@@ -59,25 +114,109 @@ module HankoXml
59
114
  end
60
115
 
61
116
  def verify(xml)
62
- xades(super)
117
+ as_xades(super)
63
118
  end
64
119
 
65
120
  def verify_all(xml)
66
- super.map { |verification| xades(verification) }
121
+ super.map { |verification| as_xades(verification) }
67
122
  end
68
123
 
69
124
  private
70
125
 
71
- def xades(verification)
126
+ # Every signature verified goes through here, with the bytes and the
127
+ # index the native layer used, which the timestamp checks reuse.
128
+ #
129
+ # With XAdES-LT values, the SignatureTimeStamps are verified first, as
130
+ # their earliest time is when the signing certificate is validated
131
+ # (#168): the XML-DSig signature is then verified at that time.
132
+ def verify_single(input, index)
133
+ values, timestamps = long_term_plan(input, index)
134
+ return xades(super, input.bytes, index) unless values
135
+
136
+ time = timestamps.map(&:time).min
137
+ verification = xades(at_time(time, values, input, index), input.bytes, index, timestamps: timestamps)
138
+ return verification unless verification.valid?
139
+
140
+ validation = LongTermCheck.new(grace: @revocation_grace, clock_skew: @policy.clock_skew)
141
+ .verify(time: time, chain: long_term_chain(verification), values: values)
142
+ rebuild(verification, signed_properties: verification.signed_properties,
143
+ signature_timestamps: verification.signature_timestamps,
144
+ archive_timestamps: verification.archive_timestamps,
145
+ long_term_validation: validation)
146
+ rescue HankoXml::XadesError, HankoXml::PolicyError => e
147
+ rebuild(verification || super, error: e)
148
+ end
149
+
150
+ # The embedded XAdES-LT values and the verified SignatureTimeStamps, or
151
+ # nil when there are no values to check.
152
+ def long_term_plan(input, index)
153
+ return nil if @long_term == :ignore
154
+
155
+ signature = signature_nodes(input)[index]
156
+ values = long_term_values(signature)
157
+ unless values
158
+ if @long_term == :require
159
+ fail!("long_term: :require, but the signature has no <xades:CertificateValues> or " \
160
+ "<xades:RevocationValues>")
161
+ end
162
+ return nil
163
+ end
164
+
165
+ timestamps = signature_timestamps(signature, input.bytes, index)
166
+ if timestamps.empty?
167
+ fail!("XAdES-LT values need a verified <xades:SignatureTimeStamp> for their validation time")
168
+ end
169
+
170
+ [values, timestamps]
171
+ end
172
+
173
+ # A signature whose QualifyingProperties or unsigned containers cannot
174
+ # be located has no values here: the signed-properties and timestamp
175
+ # checks fail it on the same structure, with their own message.
176
+ def long_term_values(signature)
177
+ qualifying, ns = Locator.qualifying_properties(signature)
178
+ properties = Locator.unsigned_signature_properties(qualifying, ns)
179
+ rescue HankoXml::XadesError
180
+ nil
181
+ else
182
+ LongTermValues.read(properties, ns)
183
+ end
184
+
185
+ # The XML-DSig verification at +time+ (unless the caller fixed
186
+ # verification_time:), with the embedded CertificateValues as untrusted
187
+ # intermediates for a store: chain.
188
+ def at_time(time, values, input, index)
189
+ verifier = AtTime.new(intermediates: values.certificates, signature_policy: @expected_signature_policy,
190
+ timestamps: :ignore, long_term: :ignore,
191
+ **@options.merge(verification_time: verification_time || time))
192
+ HankoXml::Verifier.instance_method(:verify_single).bind_call(verifier, input, index)
193
+ end
194
+
195
+ # store: gives the chain OpenSSL validated; certificate: and
196
+ # certificates: pin the signing certificate itself as the anchor.
197
+ def long_term_chain(verification)
198
+ verification.certificate_chain || [verification.certificate]
199
+ end
200
+
201
+ # A result that never reached verify_single (no signature found).
202
+ def as_xades(verification)
203
+ verification.is_a?(Verification) ? verification : rebuild(verification)
204
+ end
205
+
206
+ def xades(verification, xml_bytes, index, timestamps: nil)
72
207
  return rebuild(verification) unless verification.valid?
73
208
 
74
209
  properties = signed_properties(verification)
75
- rebuild(verification, signed_properties: properties)
210
+ timestamps ||= signature_timestamps(verification.signature_node, xml_bytes, index)
211
+ archives = archive_timestamps(verification, xml_bytes, index)
212
+ rebuild(verification, signed_properties: properties, signature_timestamps: timestamps,
213
+ archive_timestamps: archives)
76
214
  rescue HankoXml::XadesError, HankoXml::PolicyError => e
77
215
  rebuild(verification, error: e)
78
216
  end
79
217
 
80
- def rebuild(verification, signed_properties: nil, error: nil)
218
+ def rebuild(verification, signed_properties: nil, signature_timestamps: nil, long_term_validation: nil,
219
+ archive_timestamps: nil, error: nil)
81
220
  Verification.new(
82
221
  valid: verification.valid? && error.nil?,
83
222
  errors: [*verification.errors, *error],
@@ -94,10 +233,153 @@ module HankoXml
94
233
  reference_element_indexes: verification.reference_element_indexes,
95
234
  reference_valid: verification.reference_valid,
96
235
  signature_element_index: verification.signature_element_index,
97
- signed_properties: signed_properties
236
+ signed_properties: signed_properties,
237
+ signature_timestamps: signature_timestamps,
238
+ long_term_validation: long_term_validation,
239
+ archive_timestamps: archive_timestamps
98
240
  )
99
241
  end
100
242
 
243
+ # Each SignatureTimeStamp of this signature, verified (#163). Read only
244
+ # along the path from its own QualifyingProperties, never by a search
245
+ # that a wrapped element elsewhere could satisfy.
246
+ def signature_timestamps(signature, xml_bytes, index)
247
+ return nil if @timestamps == :ignore
248
+
249
+ nodes, path, xades_ns = signature_timestamp_nodes(signature)
250
+ if nodes.empty?
251
+ fail!("timestamps: :require, but the signature has no <xades:SignatureTimeStamp>") if @timestamps == :require
252
+ return []
253
+ end
254
+
255
+ unless @tsa_store
256
+ fail!("the signature has a <xades:SignatureTimeStamp>; pass tsa_store: or tsa_certificates: to " \
257
+ "verify it, or timestamps: :ignore")
258
+ end
259
+
260
+ nodes.each_with_index.map do |node, i|
261
+ SignatureTimeStampCheck.new(node, [*path, [xades_ns, "SignatureTimeStamp", i]],
262
+ ns: { "ds" => HankoXml::NS["ds"], "xades" => xades_ns },
263
+ policy: @policy, tsa_store: @tsa_store).verify(xml_bytes, index)
264
+ end
265
+ end
266
+
267
+ # Each xades141:ArchiveTimeStamp of this signature, verified (#169), read
268
+ # along the same path as the SignatureTimeStamps.
269
+ def archive_timestamps(verification, xml_bytes, index)
270
+ return nil if @timestamps == :ignore
271
+
272
+ signature = verification.signature_node
273
+ qualifying, ns = Locator.qualifying_properties(signature)
274
+ properties = Locator.unsigned_signature_properties(qualifying, ns)
275
+ nodes = properties ? properties.xpath("xades141:ArchiveTimeStamp", "xades141" => NS141).to_a : []
276
+ return [] if nodes.empty?
277
+
278
+ fail!("ArchiveTimeStamp is verified only with XAdES 1.3.2 QualifyingProperties") unless ns["xades"] == NS
279
+ unless @tsa_store
280
+ fail!("the signature has a <xades141:ArchiveTimeStamp>; pass tsa_store: or tsa_certificates: to " \
281
+ "verify it, or timestamps: :ignore")
282
+ end
283
+
284
+ path = Locator.unsigned_path(signature, qualifying, NS)
285
+ references = reference_data(verification, xml_bytes, index)
286
+ node_set = ArchiveTimeStampCheck.node_set_references?(verification.signature_info)
287
+ nodes.each_with_index.map do |node, i|
288
+ ArchiveTimeStampCheck.new(node, [*path, [NS141, "ArchiveTimeStamp", i]],
289
+ references: references, node_set_references: node_set,
290
+ element_paths: ArchiveTimeStampCheck.element_paths(signature, qualifying,
291
+ properties, node, path),
292
+ ns: { "ds" => HankoXml::NS["ds"], "xades" => NS },
293
+ policy: @policy, tsa_store: @tsa_store).verify(xml_bytes, index)
294
+ end
295
+ end
296
+
297
+ # Each SignedInfo Reference's data as xmlsec digested it, verifying the
298
+ # signature again with the key that verified it.
299
+ def reference_data(verification, xml_bytes, index)
300
+ octets = Native.reference_data(xml_bytes, [verification.certificate.public_key.to_pem], native_id_attributes,
301
+ index, @referenced_documents.to_a, @policy.allows?(:transform, :xpath_filter2))
302
+ fail!("a Reference's data could not be read for the ArchiveTimeStamp") if octets.any?(&:nil?)
303
+
304
+ octets
305
+ rescue HankoXml::NativeError
306
+ fail!("the signature's Reference data could not be read for the ArchiveTimeStamp")
307
+ end
308
+
309
+ # The SignatureTimeStamp elements, the native path to their parent, and
310
+ # their XAdES namespace.
311
+ def signature_timestamp_nodes(signature)
312
+ qualifying, ns = Locator.qualifying_properties(signature)
313
+ properties = Locator.unsigned_signature_properties(qualifying, ns)
314
+ return [[], nil] unless properties
315
+
316
+ timestamps = properties.xpath("xades:SignatureTimeStamp", ns)
317
+ if timestamps.any? && ns["xades"] != NS
318
+ fail!("SignatureTimeStamp is verified only in the XAdES 1.3.2 namespace")
319
+ end
320
+
321
+ [timestamps.to_a, Locator.unsigned_path(signature, qualifying, ns["xades"]), ns["xades"]]
322
+ end
323
+
324
+ def coerce_long_term(value)
325
+ value = @timestamps == :ignore ? :ignore : :verify if value.nil?
326
+ unless LONG_TERM_MODES.include?(value)
327
+ raise HankoXml::ConfigurationError, "long_term: must be one of #{LONG_TERM_MODES.inspect}"
328
+ end
329
+ if value != :ignore && @timestamps == :ignore
330
+ raise HankoXml::ConfigurationError,
331
+ "long_term: #{value.inspect} needs SignatureTimeStamps for its validation time; " \
332
+ "it cannot go with timestamps: :ignore"
333
+ end
334
+ if value == :require && @tsa_store.nil?
335
+ raise HankoXml::ConfigurationError, "long_term: :require needs tsa_store: or tsa_certificates:"
336
+ end
337
+
338
+ value
339
+ end
340
+
341
+ def coerce_revocation_grace(value)
342
+ return value if value.is_a?(Numeric) && value >= 0
343
+
344
+ raise HankoXml::ConfigurationError, "revocation_grace: must be a number of seconds, 0 or more"
345
+ end
346
+
347
+ def coerce_timestamps(value)
348
+ return value if TIMESTAMP_MODES.include?(value)
349
+
350
+ raise HankoXml::ConfigurationError, "timestamps: must be one of #{TIMESTAMP_MODES.inspect}"
351
+ end
352
+
353
+ # The TSA trust, separate from the signer's: tsa_store: as given, or a
354
+ # store over tsa_certificates:.
355
+ def coerce_tsa_store(store, certificates)
356
+ raise HankoXml::ConfigurationError, "pass tsa_store: or tsa_certificates:, not both" if store && certificates
357
+
358
+ if @timestamps == :require && !store && !certificates
359
+ raise HankoXml::ConfigurationError, "timestamps: :require needs tsa_store: or tsa_certificates:"
360
+ end
361
+ return tsa_store_from(certificates) if store.nil?
362
+ return store if store.is_a?(OpenSSL::X509::Store)
363
+
364
+ raise HankoXml::ConfigurationError, "tsa_store: must be an OpenSSL::X509::Store"
365
+ end
366
+
367
+ def tsa_store_from(certificates)
368
+ return nil if certificates.nil?
369
+
370
+ list = Array(certificates)
371
+ raise HankoXml::ConfigurationError, "tsa_certificates: must not be empty" if list.empty?
372
+
373
+ OpenSSL::X509::Store.new.tap do |store|
374
+ list.each do |certificate|
375
+ key = HankoXml::Key.coerce(certificate)
376
+ raise HankoXml::ConfigurationError, "tsa_certificates: must be X.509 certificates" unless key.certificate?
377
+
378
+ store.add_cert(key.to_openssl)
379
+ end
380
+ end
381
+ end
382
+
101
383
  def signed_properties(verification)
102
384
  node, ns = signed_properties_node(verification)
103
385
  signature_properties = node.at_xpath("xades:SignedSignatureProperties", ns)
@@ -122,7 +404,7 @@ module HankoXml
122
404
  # with.
123
405
  def signed_properties_node(verification)
124
406
  info, reference = signed_properties_reference(verification)
125
- qualifying, ns = qualifying_properties(verification.signature_node)
407
+ qualifying, ns = Locator.qualifying_properties(verification.signature_node)
126
408
 
127
409
  type_namespace = VERSIONED_SIGNED_PROPERTIES_TYPES[info.type]
128
410
  if type_namespace && type_namespace != ns["xades"]
@@ -153,22 +435,6 @@ module HankoXml
153
435
  pairs.first
154
436
  end
155
437
 
156
- # This signature's single QualifyingProperties, in any XAdES namespace,
157
- # and the namespaces to read it with.
158
- def qualifying_properties(signature)
159
- signature_id = signature["Id"].to_s
160
- fail!("the <Signature> has no Id for QualifyingProperties to target") if signature_id.empty?
161
-
162
- qualifying = signature.xpath("ds:Object/*[local-name()='QualifyingProperties']", "ds" => HankoXml::NS["ds"])
163
- .select { |node| NAMESPACES.include?(node.namespace&.href) }
164
- fail!("a XAdES signature has exactly one <xades:QualifyingProperties>") unless qualifying.size == 1
165
-
166
- qualifying = qualifying.first
167
- fail!("<xades:QualifyingProperties> does not target this <Signature>") unless targets?(qualifying, signature_id)
168
-
169
- [qualifying, { "ds" => HankoXml::NS["ds"], "xades" => qualifying.namespace.href }]
170
- end
171
-
172
438
  # By its Type, or, with no Type (as some 1.2.2 signatures write it), by
173
439
  # what xmlsec resolved and digested: a SignedProperties element in a
174
440
  # XAdES namespace. Never by the URI text (#86).
@@ -179,16 +445,6 @@ module HankoXml
179
445
  !node.nil? && node.name == "SignedProperties" && NAMESPACES.include?(node.namespace&.href)
180
446
  end
181
447
 
182
- # Target is "#" + the Signature Id. The older namespaces also had
183
- # signers write the bare Id (Target="Signature"). Either way the
184
- # QualifyingProperties must sit in this <Signature>'s own <Object>.
185
- def targets?(qualifying, signature_id)
186
- target = qualifying["Target"]
187
- return true if target == "##{signature_id}"
188
-
189
- LEGACY_NAMESPACES.include?(qualifying.namespace.href) && target == signature_id
190
- end
191
-
192
448
  def check_signing_certificate!(signature_properties, certificate, ns)
193
449
  fail!("no certificate verified the signature, so SigningCertificate cannot be checked") unless certificate
194
450
 
@@ -314,6 +570,24 @@ module HankoXml
314
570
  def fail!(message)
315
571
  raise HankoXml::XadesError, message
316
572
  end
573
+
574
+ # The XML-DSig verification of an XAdES-LT signature (#168): embedded
575
+ # certificates may complete the store: chain, never be its anchor or
576
+ # its signing certificate.
577
+ #
578
+ # @api private
579
+ class AtTime < Verifier
580
+ def initialize(intermediates:, **options)
581
+ @intermediates = intermediates
582
+ super(**options)
583
+ end
584
+
585
+ private
586
+
587
+ def chain_intermediates
588
+ @intermediates
589
+ end
590
+ end
317
591
  end
318
592
  end
319
593
  end