Skip to content

Commit 2521c88

Browse files
committed
Organize and autodoc enums
1 parent 78aa305 commit 2521c88

File tree

8 files changed

+99
-77
lines changed

8 files changed

+99
-77
lines changed

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ API documentation
66
.. automodule:: signxml
77
:members:
88
:imported-members:
9+
:undoc-members:
910

1011
Change log
1112
==========

signxml/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# isort: skip_file
22
from .signer import XMLSigner # noqa:F401
33
from .verifier import XMLVerifier, VerifyResult # noqa:F401
4-
from .algorithms import XMLSecurityDigestAlgorithm as digest_algorithms # noqa:F401
5-
from .algorithms import XMLSecuritySignatureMethod as signature_methods # noqa:F401
6-
from .algorithms import XMLSignatureMethods as methods # noqa:F401
4+
from .algorithms import DigestAlgorithm, SignatureMethod, SignatureType # noqa:F401
75
from .exceptions import InvalidCertificate, InvalidDigest, InvalidInput, InvalidSignature # noqa:F401
86
from .processor import XMLSignatureProcessor # noqa:F401
97
from .util import SigningSettings, namespaces # noqa:F401
8+
9+
methods = SignatureType

signxml/algorithms.py

Lines changed: 63 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,30 @@
55
from .exceptions import InvalidInput
66

77

8-
class XMLSignatureMethods(Enum):
8+
class SignatureType(Enum):
9+
"""
10+
An enumeration of the structural type of signature supported by SignXML.
11+
"""
12+
913
enveloped = auto()
14+
"""
15+
The signature is over the XML content that contains the signature as an element. The content provides the root
16+
XML document element.
17+
"""
18+
1019
enveloping = auto()
20+
"""
21+
The signature is over content found within an Object element of the signature itself. The Object (or its
22+
content) is identified via a Reference (via a URI fragment identifier or transform).
23+
"""
24+
1125
detached = auto()
26+
"""
27+
The signature is over content external to the Signature element, and can be identified via a URI or
28+
transform. Consequently, the signature is "detached" from the content it signs. This definition typically applies to
29+
separate data objects, but it also includes the instance where the Signature and data object reside within the same
30+
XML document but are sibling elements.
31+
"""
1232

1333

1434
class FragmentLookupMixin:
@@ -27,7 +47,11 @@ def _missing_(cls, value):
2747
raise InvalidInput(f"Unrecognized {cls.__name__}: {value}")
2848

2949

30-
class XMLSecurityDigestAlgorithm(FragmentLookupMixin, InvalidInputErrorMixin, Enum):
50+
class DigestAlgorithm(FragmentLookupMixin, InvalidInputErrorMixin, Enum):
51+
"""
52+
An enumeration of digest algorithms supported by SignXML. See RFC 9231 for details.
53+
"""
54+
3155
SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1"
3256
SHA224 = "http://www.w3.org/2001/04/xmldsig-more#sha224"
3357
SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"
@@ -44,7 +68,11 @@ def implementation(self):
4468

4569

4670
# TODO: check if padding errors are fixed by using padding=MGF1
47-
class XMLSecuritySignatureMethod(FragmentLookupMixin, InvalidInputErrorMixin, Enum):
71+
class SignatureMethod(FragmentLookupMixin, InvalidInputErrorMixin, Enum):
72+
"""
73+
An enumeration of signature methods supported by SignXML. See RFC 9231 for details.
74+
"""
75+
4876
DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1"
4977
HMAC_SHA1 = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"
5078
RSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
@@ -72,36 +100,36 @@ class XMLSecuritySignatureMethod(FragmentLookupMixin, InvalidInputErrorMixin, En
72100

73101

74102
digest_algorithm_implementations = {
75-
XMLSecurityDigestAlgorithm.SHA1: hashes.SHA1,
76-
XMLSecurityDigestAlgorithm.SHA224: hashes.SHA224,
77-
XMLSecurityDigestAlgorithm.SHA384: hashes.SHA384,
78-
XMLSecurityDigestAlgorithm.SHA256: hashes.SHA256,
79-
XMLSecurityDigestAlgorithm.SHA512: hashes.SHA512,
80-
XMLSecurityDigestAlgorithm.SHA3_224: hashes.SHA3_224,
81-
XMLSecurityDigestAlgorithm.SHA3_256: hashes.SHA3_256,
82-
XMLSecurityDigestAlgorithm.SHA3_384: hashes.SHA3_384,
83-
XMLSecurityDigestAlgorithm.SHA3_512: hashes.SHA3_512,
84-
XMLSecuritySignatureMethod.DSA_SHA1: hashes.SHA1,
85-
XMLSecuritySignatureMethod.HMAC_SHA1: hashes.SHA1,
86-
XMLSecuritySignatureMethod.RSA_SHA1: hashes.SHA1,
87-
XMLSecuritySignatureMethod.ECDSA_SHA1: hashes.SHA1,
88-
XMLSecuritySignatureMethod.ECDSA_SHA224: hashes.SHA224,
89-
XMLSecuritySignatureMethod.ECDSA_SHA256: hashes.SHA256,
90-
XMLSecuritySignatureMethod.ECDSA_SHA384: hashes.SHA384,
91-
XMLSecuritySignatureMethod.ECDSA_SHA512: hashes.SHA512,
92-
XMLSecuritySignatureMethod.HMAC_SHA224: hashes.SHA224,
93-
XMLSecuritySignatureMethod.HMAC_SHA256: hashes.SHA256,
94-
XMLSecuritySignatureMethod.HMAC_SHA384: hashes.SHA384,
95-
XMLSecuritySignatureMethod.HMAC_SHA512: hashes.SHA512,
96-
XMLSecuritySignatureMethod.RSA_SHA224: hashes.SHA224,
97-
XMLSecuritySignatureMethod.RSA_SHA256: hashes.SHA256,
98-
XMLSecuritySignatureMethod.RSA_SHA384: hashes.SHA384,
99-
XMLSecuritySignatureMethod.RSA_SHA512: hashes.SHA512,
100-
XMLSecuritySignatureMethod.DSA_SHA256: hashes.SHA256,
101-
XMLSecuritySignatureMethod.ECDSA_SHA3_224: hashes.SHA1,
102-
XMLSecuritySignatureMethod.ECDSA_SHA3_256: hashes.SHA1,
103-
XMLSecuritySignatureMethod.ECDSA_SHA3_384: hashes.SHA1,
104-
XMLSecuritySignatureMethod.ECDSA_SHA3_512: hashes.SHA1,
105-
XMLSecuritySignatureMethod.EDDSA_ED25519: hashes.SHA512,
106-
XMLSecuritySignatureMethod.EDDSA_ED448: hashes.SHAKE256,
103+
DigestAlgorithm.SHA1: hashes.SHA1,
104+
DigestAlgorithm.SHA224: hashes.SHA224,
105+
DigestAlgorithm.SHA384: hashes.SHA384,
106+
DigestAlgorithm.SHA256: hashes.SHA256,
107+
DigestAlgorithm.SHA512: hashes.SHA512,
108+
DigestAlgorithm.SHA3_224: hashes.SHA3_224,
109+
DigestAlgorithm.SHA3_256: hashes.SHA3_256,
110+
DigestAlgorithm.SHA3_384: hashes.SHA3_384,
111+
DigestAlgorithm.SHA3_512: hashes.SHA3_512,
112+
SignatureMethod.DSA_SHA1: hashes.SHA1,
113+
SignatureMethod.HMAC_SHA1: hashes.SHA1,
114+
SignatureMethod.RSA_SHA1: hashes.SHA1,
115+
SignatureMethod.ECDSA_SHA1: hashes.SHA1,
116+
SignatureMethod.ECDSA_SHA224: hashes.SHA224,
117+
SignatureMethod.ECDSA_SHA256: hashes.SHA256,
118+
SignatureMethod.ECDSA_SHA384: hashes.SHA384,
119+
SignatureMethod.ECDSA_SHA512: hashes.SHA512,
120+
SignatureMethod.HMAC_SHA224: hashes.SHA224,
121+
SignatureMethod.HMAC_SHA256: hashes.SHA256,
122+
SignatureMethod.HMAC_SHA384: hashes.SHA384,
123+
SignatureMethod.HMAC_SHA512: hashes.SHA512,
124+
SignatureMethod.RSA_SHA224: hashes.SHA224,
125+
SignatureMethod.RSA_SHA256: hashes.SHA256,
126+
SignatureMethod.RSA_SHA384: hashes.SHA384,
127+
SignatureMethod.RSA_SHA512: hashes.SHA512,
128+
SignatureMethod.DSA_SHA256: hashes.SHA256,
129+
SignatureMethod.ECDSA_SHA3_224: hashes.SHA1,
130+
SignatureMethod.ECDSA_SHA3_256: hashes.SHA1,
131+
SignatureMethod.ECDSA_SHA3_384: hashes.SHA1,
132+
SignatureMethod.ECDSA_SHA3_512: hashes.SHA1,
133+
SignatureMethod.EDDSA_ED25519: hashes.SHA512,
134+
SignatureMethod.EDDSA_ED448: hashes.SHAKE256,
107135
}

signxml/processor.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
from cryptography.hazmat.primitives.hashes import Hash
77
from lxml import etree
88

9-
from .algorithms import XMLSecurityDigestAlgorithm as digest_algorithms
10-
from .algorithms import digest_algorithm_implementations
9+
from .algorithms import DigestAlgorithm, digest_algorithm_implementations
1110
from .exceptions import InvalidInput
1211
from .util import namespaces
1312

@@ -89,7 +88,7 @@ class XMLSignatureProcessor(XMLProcessor):
8988

9089
id_attributes: Tuple[str, ...] = ("Id", "ID", "id", "xml:id")
9190

92-
def _get_digest(self, data, algorithm: digest_algorithms):
91+
def _get_digest(self, data, algorithm: DigestAlgorithm):
9392
algorithm_implementation = digest_algorithm_implementations[algorithm]()
9493
hasher = Hash(algorithm=algorithm_implementation)
9594
hasher.update(data)

signxml/signer.py

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
from lxml.etree import Element, SubElement
99
from OpenSSL.crypto import FILETYPE_PEM, dump_certificate
1010

11-
from .algorithms import XMLSecurityDigestAlgorithm as digest_algorithms
12-
from .algorithms import XMLSecuritySignatureMethod as signature_methods
13-
from .algorithms import XMLSignatureMethods as methods
14-
from .algorithms import digest_algorithm_implementations
11+
from .algorithms import DigestAlgorithm, SignatureMethod, SignatureType, digest_algorithm_implementations
1512
from .exceptions import InvalidInput
1613
from .processor import XMLSignatureProcessor
1714
from .util import (
@@ -38,37 +35,34 @@ class XMLSigner(XMLSignatureProcessor):
3835
``signxml.methods.enveloped``, ``signxml.methods.enveloping``, or ``signxml.methods.detached``. See the list
3936
of signature types under `XML Signature Syntax and Processing Version 2.0, Definitions
4037
<http://www.w3.org/TR/xmldsig-core2/#sec-Definitions>`_.
41-
:type method: :py:class:`methods`
4238
:param signature_algorithm:
4339
Algorithm that will be used to generate the signature, composed of the signature algorithm and the digest
4440
algorithm, separated by a hyphen. All algorithm IDs listed under the `Algorithm Identifiers and
4541
Implementation Requirements <http://www.w3.org/TR/xmldsig-core1/#sec-AlgID>`_ section of the XML Signature
4642
1.1 standard are supported.
47-
:type signature_algorithm: string
4843
:param digest_algorithm: Algorithm that will be used to hash the data during signature generation. All algorithm IDs
4944
listed under the `Algorithm Identifiers and Implementation Requirements
5045
<http://www.w3.org/TR/xmldsig-core1/#sec-AlgID>`_ section of the XML Signature 1.1 standard are supported.
51-
:type digest_algorithm: string
5246
"""
5347

5448
def __init__(
5549
self,
56-
method: methods = methods.enveloped,
57-
signature_algorithm: Union[signature_methods, str] = signature_methods.RSA_SHA256,
58-
digest_algorithm: Union[digest_algorithms, str] = digest_algorithms.SHA256,
50+
method: SignatureType = SignatureType.enveloped,
51+
signature_algorithm: Union[SignatureMethod, str] = SignatureMethod.RSA_SHA256,
52+
digest_algorithm: Union[DigestAlgorithm, str] = DigestAlgorithm.SHA256,
5953
c14n_algorithm=XMLSignatureProcessor.default_c14n_algorithm,
6054
):
61-
if method is None or method not in methods:
55+
if method is None or method not in SignatureType:
6256
raise InvalidInput("Unknown signature method {}".format(method))
63-
self.method = method
57+
self.signature_type = method
6458
if isinstance(signature_algorithm, str) and "#" not in signature_algorithm:
65-
self.sign_alg = signature_methods.from_fragment(signature_algorithm)
59+
self.sign_alg = SignatureMethod.from_fragment(signature_algorithm)
6660
else:
67-
self.sign_alg = signature_methods(signature_algorithm)
61+
self.sign_alg = SignatureMethod(signature_algorithm)
6862
if isinstance(digest_algorithm, str) and "#" not in digest_algorithm:
69-
self.digest_alg = digest_algorithms.from_fragment(digest_algorithm)
63+
self.digest_alg = DigestAlgorithm.from_fragment(digest_algorithm)
7064
else:
71-
self.digest_alg = digest_algorithms(digest_algorithm)
65+
self.digest_alg = DigestAlgorithm(digest_algorithm)
7266
assert c14n_algorithm in self.known_c14n_algorithms
7367
self.c14n_alg = c14n_algorithm
7468
self.namespaces = dict(ds=namespaces.ds)
@@ -189,7 +183,7 @@ def sign(
189183

190184
sig_root, doc_root, c14n_inputs, reference_uris = self._unpack(data, reference_uris)
191185

192-
if self.method == methods.detached and signature_properties is not None:
186+
if self.signature_type == SignatureType.detached and signature_properties is not None:
193187
reference_uris.append("#prop")
194188
if signature_properties is not None and not isinstance(signature_properties, list):
195189
signature_properties = [signature_properties]
@@ -237,14 +231,14 @@ def sign(
237231
else:
238232
raise NotImplementedError()
239233

240-
if self.method == methods.enveloping:
234+
if self.signature_type == SignatureType.enveloping:
241235
for c14n_input in c14n_inputs:
242236
doc_root.append(c14n_input)
243237

244-
if self.method == methods.detached and signature_properties is not None:
238+
if self.signature_type == SignatureType.detached and signature_properties is not None:
245239
sig_root.append(signature_properties_el)
246240

247-
return doc_root if self.method == methods.enveloped else sig_root
241+
return doc_root if self.signature_type == SignatureType.enveloped else sig_root
248242

249243
def _add_key_info(self, sig_root, signing_settings: SigningSettings):
250244
if self.sign_alg.name.startswith("HMAC_"):
@@ -280,7 +274,7 @@ def _get_c14n_inputs_from_reference_uris(self, doc_root, reference_uris):
280274

281275
def _unpack(self, data, reference_uris):
282276
sig_root = Element(ds_tag("Signature"), nsmap=self.namespaces)
283-
if self.method == methods.enveloped:
277+
if self.signature_type == SignatureType.enveloped:
284278
if isinstance(data, (str, bytes)):
285279
raise InvalidInput("When using enveloped signature, **data** must be an XML element")
286280
doc_root = self.get_root(data)
@@ -309,7 +303,7 @@ def _unpack(self, data, reference_uris):
309303
for c14n_input in c14n_inputs:
310304
payload_id = c14n_input.get("Id", c14n_input.get("ID"))
311305
reference_uris.append("#{}".format(payload_id) if payload_id is not None else "")
312-
elif self.method == methods.detached:
306+
elif self.signature_type == SignatureType.detached:
313307
doc_root = self.get_root(data)
314308
if reference_uris is None:
315309
reference_uris = ["#{}".format(data.get("Id", data.get("ID", "object")))]
@@ -318,7 +312,7 @@ def _unpack(self, data, reference_uris):
318312
c14n_inputs, reference_uris = self._get_c14n_inputs_from_reference_uris(doc_root, reference_uris)
319313
except InvalidInput: # Dummy reference URI
320314
c14n_inputs = [self.get_root(data)]
321-
elif self.method == methods.enveloping:
315+
elif self.signature_type == SignatureType.enveloping:
322316
doc_root = sig_root
323317
c14n_inputs = [Element(ds_tag("Object"), nsmap=self.namespaces, Id="object")]
324318
if isinstance(data, (str, bytes)):
@@ -338,7 +332,7 @@ def _build_sig(self, sig_root, reference_uris, c14n_inputs, sig_insp, payload_in
338332
for i, reference_uri in enumerate(reference_uris):
339333
reference = SubElement(signed_info, ds_tag("Reference"), URI=reference_uri)
340334
transforms = SubElement(reference, ds_tag("Transforms"))
341-
if self.method == methods.enveloped:
335+
if self.signature_type == SignatureType.enveloped:
342336
SubElement(transforms, ds_tag("Transform"), Algorithm=namespaces.ds + "enveloped-signature")
343337
SubElement(transforms, ds_tag("Transform"), Algorithm=self.c14n_alg)
344338
else:

signxml/verifier.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
from OpenSSL.crypto import load_certificate
1313
from OpenSSL.crypto import verify as openssl_verify
1414

15-
from .algorithms import XMLSecurityDigestAlgorithm as digest_algorithms
16-
from .algorithms import XMLSecuritySignatureMethod as signature_methods
17-
from .algorithms import digest_algorithm_implementations
15+
from .algorithms import DigestAlgorithm, SignatureMethod, digest_algorithm_implementations
1816
from .exceptions import InvalidCertificate, InvalidDigest, InvalidInput, InvalidSignature # noqa
1917
from .processor import XMLSignatureProcessor
2018
from .util import (
@@ -280,7 +278,7 @@ def verify(
280278
inclusive_ns_prefixes = self._get_inclusive_ns_prefixes(c14n_method)
281279
signature_method = self._find(signed_info, "SignatureMethod")
282280
signature_value = self._find(signature, "SignatureValue")
283-
signature_alg = signature_methods(signature_method.get("Algorithm"))
281+
signature_alg = SignatureMethod(signature_method.get("Algorithm"))
284282
raw_signature = b64decode(signature_value.text)
285283
x509_data = signature.find("ds:KeyInfo/ds:X509Data", namespaces=namespaces)
286284
key_value = signature.find("ds:KeyInfo/ds:KeyValue", namespaces=namespaces)
@@ -396,7 +394,7 @@ def verify(
396394
digest_value = self._find(reference, "DigestValue")
397395
payload = self._resolve_reference(copied_root, reference, uri_resolver=uri_resolver)
398396
payload_c14n = self._apply_transforms(payload, transforms, copied_signature_ref, c14n_algorithm)
399-
if b64decode(digest_value.text) != self._get_digest(payload_c14n, digest_algorithms(digest_alg)):
397+
if b64decode(digest_value.text) != self._get_digest(payload_c14n, DigestAlgorithm(digest_alg)):
400398
raise InvalidDigest(f"Digest mismatch for reference {len(verify_results)} ({reference.get('URI')})")
401399

402400
# We return the signed XML (and only that) to ensure no access to unsigned data happens
@@ -422,7 +420,7 @@ def validate_schema(self, signature):
422420
last_exception = e
423421
raise last_exception # type: ignore
424422

425-
def check_key_value_matches_cert_public_key(self, key_value, public_key, signature_alg: signature_methods):
423+
def check_key_value_matches_cert_public_key(self, key_value, public_key, signature_alg: SignatureMethod):
426424
if signature_alg.name.startswith("ECDSA_") and isinstance(
427425
public_key.to_cryptography_key(), ec.EllipticCurvePublicKey
428426
):

signxml/xades/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
from OpenSSL.crypto import FILETYPE_ASN1, FILETYPE_PEM, X509, dump_certificate, load_certificate
5151

5252
from .. import VerifyResult, XMLSignatureProcessor, XMLSigner, XMLVerifier
53-
from ..algorithms import XMLSecurityDigestAlgorithm as digest_algorithms
53+
from ..algorithms import DigestAlgorithm
5454
from ..exceptions import InvalidDigest, InvalidInput
5555
from ..util import SigningSettings, add_pem_header, ds_tag, namespaces, xades_tag
5656

@@ -195,7 +195,7 @@ def add_signature_policy_identifier(self, signed_signature_properties, sig_root,
195195
description = SubElement(sig_policy_id, xades_tag("Description"), nsmap=self.namespaces)
196196
description.text = self.signature_policy["Description"]
197197
sig_policy_hash = SubElement(signature_policy_id, xades_tag("SigPolicyHash"), nsmap=self.namespaces)
198-
digest_alg = digest_algorithms(self.signature_policy["DigestMethod"])
198+
digest_alg = DigestAlgorithm(self.signature_policy["DigestMethod"])
199199
SubElement(sig_policy_hash, ds_tag("DigestMethod"), nsmap=self.namespaces, Algorithm=digest_alg.value)
200200
digest_value_node = SubElement(sig_policy_hash, ds_tag("DigestValue"), nsmap=self.namespaces)
201201
digest_value_node.text = self.signature_policy["DigestValue"]
@@ -245,7 +245,7 @@ def _verify_signing_time(self, verify_result: VerifyResult):
245245
def _verify_cert_digest(self, signing_cert_node, expect_cert):
246246
for cert in self._findall(signing_cert_node, "xades:Cert"):
247247
cert_digest = self._find(cert, "xades:CertDigest")
248-
digest_alg = digest_algorithms(self._find(cert_digest, "DigestMethod").get("Algorithm"))
248+
digest_alg = DigestAlgorithm(self._find(cert_digest, "DigestMethod").get("Algorithm"))
249249
digest_value = self._find(cert_digest, "DigestValue")
250250
# check spec for specific method of retrieving cert
251251
der_encoded_cert = dump_certificate(FILETYPE_ASN1, expect_cert)
@@ -286,7 +286,7 @@ def _verify_signature_policy(self, verify_result: VerifyResult):
286286
f"but found {identifier.text}"
287287
)
288288
sig_policy_hash = self._find(signature_policy_id, "xades:SigPolicyHash")
289-
digest_alg = digest_algorithms(self._find(sig_policy_hash, "DigestMethod").get("Algorithm"))
289+
digest_alg = DigestAlgorithm(self._find(sig_policy_hash, "DigestMethod").get("Algorithm"))
290290
if digest_alg != self.expect_signature_policy["DigestMethod"]:
291291
raise InvalidInput(
292292
f"Expected to find signature digest algorithm {self.expect_signature_policy['DigestMethod']}, "

0 commit comments

Comments
 (0)