Skip to content

Commit 6896f64

Browse files
committed
WIP
1 parent 439a908 commit 6896f64

File tree

4 files changed

+43
-16
lines changed

4 files changed

+43
-16
lines changed

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"cryptography >= 3.4.8", # Set to the version in Ubuntu 22.04 due to features we need from cryptography 3.1
2020
"pyOpenSSL >= 17.5.0",
2121
"certifi >= 2018.1.18",
22+
"tsp-client >= 0.1.3",
2223
],
2324
extras_require={
2425
"tests": [

signxml/util/__init__.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
from dataclasses import dataclass
1313
from typing import Any, List, Optional
1414

15+
import certifi
1516
from cryptography.hazmat.primitives import hashes, hmac
1617
from lxml.etree import QName
18+
from OpenSSL.crypto import Error as OpenSSLCryptoError
19+
from OpenSSL.crypto import X509Store, X509StoreContext, X509StoreContextError
1720

1821
from ..exceptions import InvalidCertificate, RedundantCert, SignXMLException
1922

@@ -207,9 +210,6 @@ def p_sha1(client_b64_bytes, server_b64_bytes):
207210

208211

209212
def _add_cert_to_store(store, cert):
210-
from OpenSSL.crypto import Error as OpenSSLCryptoError
211-
from OpenSSL.crypto import X509StoreContext, X509StoreContextError
212-
213213
try:
214214
X509StoreContext(store, cert).verify_certificate()
215215
except X509StoreContextError as e:
@@ -233,22 +233,21 @@ def verify_x509_cert_chain(cert_chain, ca_pem_file=None, ca_path=None):
233233
No ordering is implied by the above constraints"
234234
"""
235235
# TODO: migrate to Cryptography (pending cert validation support) or https://github.com/wbond/certvalidator
236-
from OpenSSL import SSL
237-
238-
context = SSL.Context(SSL.TLSv1_METHOD)
236+
x509_store = X509Store()
239237
if ca_pem_file is None and ca_path is None:
240-
import certifi
241-
242238
ca_pem_file = certifi.where()
243-
context.load_verify_locations(ensure_bytes(ca_pem_file, none_ok=True), capath=ca_path)
244-
store = context.get_cert_store()
239+
x509_store.load_locations(cafile=ca_pem_file, capath=ca_path)
240+
241+
# FIXME: use X509StoreContext(store=x509_store, certificate=cert, chain=cert_chain).get_verified_chain()
242+
# This requires identifying the signing cert out-of-band
243+
245244
certs = list(reversed(cert_chain))
246245
end_of_chain = None
247246
last_error: Exception = SignXMLException("Invalid certificate chain")
248247
while len(certs) > 0:
249248
for cert in certs:
250249
try:
251-
end_of_chain = _add_cert_to_store(store, cert)
250+
end_of_chain = _add_cert_to_store(x509_store, cert)
252251
certs.remove(cert)
253252
break
254253
except RedundantCert:

signxml/verifier.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ def verify(
375375
raise InvalidInput(msg)
376376
else:
377377
cert_chain = [load_certificate(FILETYPE_PEM, add_pem_header(cert)) for cert in certs]
378+
# FIXME: switch to wbondcrypto cert chain verify
378379
signing_cert = verify_x509_cert_chain(cert_chain, ca_pem_file=ca_pem_file, ca_path=ca_path)
379380
elif isinstance(self.x509_cert, X509):
380381
signing_cert = self.x509_cert
@@ -389,6 +390,8 @@ def verify(
389390

390391
try:
391392
digest_alg_name = str(digest_algorithm_implementations[signature_alg].name)
393+
# FIXME: confirm the specified signature algorithm matches the certificate's public key
394+
# FIXME: switch to cryptography verify
392395
openssl_verify(signing_cert, raw_signature, signed_info_c14n, digest_alg_name)
393396
except OpenSSLCryptoError as e:
394397
try:

signxml/xades/xades.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
from lxml.etree import SubElement, _Element
3131
from OpenSSL.crypto import FILETYPE_ASN1, FILETYPE_PEM, X509, dump_certificate, load_certificate
32+
from tsp_client import TSPVerifier
3233

3334
from .. import SignatureConfiguration, VerifyResult, XMLSignatureProcessor, XMLSigner, XMLVerifier
3435
from ..algorithms import DigestAlgorithm
@@ -264,9 +265,31 @@ class XAdESVerifier(XAdESProcessor, XMLVerifier):
264265
"""
265266

266267
# TODO: document/support SignatureTimeStamp / timestamp attestation
268+
# TODO: allow setting required attributes, including timestamp
267269
# SignatureTimeStamp is required by certain profiles but is an unsigned property
268-
def _verify_signing_time(self, verify_result: VerifyResult):
269-
pass
270+
271+
def _verify_signing_time(self, verify_result: VerifyResult, all_verify_results: List[VerifyResult]):
272+
"""
273+
The Implicit mechanism (see clause 5.1.4.4.1) shall be used for generating this qualifying property.
274+
The input to the computation of the message imprint shall be the result of processing all the ds:Reference
275+
elements within the ds:SignedInfo except the one referencing the SignedProperties element, in their order of
276+
appearance, as follows:
277+
1) process the retrieved ds:Reference element according to the reference-processing model of XMLDSIG [1]
278+
clause 4.4.3.2;
279+
2) if the result is a XML node set, canonicalize it as specified in clause 4.5; and
280+
3) concatenate the resulting octets to those resulting from previously processed ds:Reference elements in
281+
ds:SignedInfo.
282+
"""
283+
ts_path = "xades:SignedDataObjectProperties/xades:AllDataObjectsTimeStamp/xades:EncapsulatedTimeStamp"
284+
if verify_result.signed_xml is None:
285+
return
286+
all_data_objs_ts = verify_result.signed_xml.find(ts_path, namespaces=namespaces)
287+
if all_data_objs_ts is None:
288+
return
289+
print("Will verify", all_data_objs_ts.text)
290+
ts = b64decode(all_data_objs_ts.text) # type: ignore
291+
tsp_message = b"".join(r.signed_data for r in all_verify_results if r != verify_result)
292+
TSPVerifier().verify(ts, message=tsp_message)
270293

271294
def _verify_cert_digest(self, signing_cert_node, expect_cert):
272295
for cert in self._findall(signing_cert_node, "xades:Cert"):
@@ -320,8 +343,8 @@ def _verify_signature_policy(self, verify_result: VerifyResult, expect_signature
320343
if b64decode(digest_value.text) != b64decode(expect_signature_policy.DigestValue):
321344
raise InvalidInput("Digest mismatch for signature policy hash")
322345

323-
def _verify_signed_properties(self, verify_result):
324-
self._verify_signing_time(verify_result)
346+
def _verify_signed_properties(self, verify_result, *, all_verify_results):
347+
self._verify_signing_time(verify_result, all_verify_results=all_verify_results)
325348
self._verify_cert_digests(verify_result)
326349
if self.expect_signature_policy:
327350
self._verify_signature_policy(
@@ -364,7 +387,8 @@ def verify( # type: ignore
364387
continue
365388
if verify_result.signed_xml.tag == xades_tag("SignedProperties"):
366389
verify_results[i] = XAdESVerifyResult( # type: ignore
367-
*astuple(verify_result), signed_properties=self._verify_signed_properties(verify_result)
390+
*astuple(verify_result),
391+
signed_properties=self._verify_signed_properties(verify_result, all_verify_results=verify_results),
368392
)
369393
break
370394
else:

0 commit comments

Comments
 (0)