From 6b21a86cd6297b0cb96514c7c1573f629b5afbd0 Mon Sep 17 00:00:00 2001 From: Andrey Kislyuk Date: Sun, 13 Nov 2022 00:10:37 -0800 Subject: [PATCH] Documentation improvements --- docs/conf.py | 8 +++++++- signxml/algorithms.py | 13 +++++++++---- signxml/signer.py | 45 +++++++++++++++++++++---------------------- signxml/verifier.py | 4 ++-- 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f7e47f2..664f52b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,7 +7,7 @@ release = "" language = "en" master_doc = "index" -extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode"] +extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "sphinx.ext.intersphinx"] source_suffix = [".rst", ".md"] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] pygments_style = "sphinx" @@ -15,6 +15,12 @@ autodoc_typehints = "description" typehints_fully_qualified = True always_document_param_types = True +intersphinx_mapping = { + "https://docs.python.org/3": None, + "https://lxml.de/apidoc": "https://lxml.de/apidoc/objects.inv", + "https://cryptography.io/en/latest": "https://cryptography.io/en/latest/objects.inv", + "https://www.pyopenssl.org/en/stable": "https://www.pyopenssl.org/en/stable/objects.inv", +} if "readthedocs.org" in os.getcwd().split("/"): with open("index.rst", "w") as fh: diff --git a/signxml/algorithms.py b/signxml/algorithms.py index ce6ccdf..59ef336 100644 --- a/signxml/algorithms.py +++ b/signxml/algorithms.py @@ -52,7 +52,9 @@ def _missing_(cls, value): class DigestAlgorithm(FragmentLookupMixin, InvalidInputErrorMixin, Enum): """ - An enumeration of digest algorithms supported by SignXML. See RFC 9231 for details. + An enumeration of digest algorithms supported by SignXML. See `RFC 9231 + `_ and the `Algorithm Identifiers and Implementation Requirements + `_ section of the XML Signature 1.1 standard for details. """ SHA1 = "http://www.w3.org/2000/09/xmldsig#sha1" @@ -76,8 +78,9 @@ def implementation(self) -> Callable: # TODO: check if padding errors are fixed by using padding=MGF1 class SignatureMethod(FragmentLookupMixin, InvalidInputErrorMixin, Enum): """ - An enumeration of signature methods (also referred to as signature algorithms) supported by SignXML. See RFC 9231 - for details. + An enumeration of signature methods (also referred to as signature algorithms) supported by SignXML. See `RFC 9231 + `_ and the `Algorithm Identifiers and Implementation Requirements + `_ section of the XML Signature 1.1 standard for details. """ DSA_SHA1 = "http://www.w3.org/2000/09/xmldsig#dsa-sha1" @@ -109,7 +112,9 @@ class SignatureMethod(FragmentLookupMixin, InvalidInputErrorMixin, Enum): class CanonicalizationMethod(InvalidInputErrorMixin, Enum): """ An enumeration of XML canonicalization methods (also referred to as canonicalization algorithms) supported by - SignXML. See RFC 9231 for details. + SignXML. See `RFC 9231 `_ and the `Algorithm Identifiers and + Implementation Requirements `_ section of the XML Signature 1.1 + standard for details. """ CANONICAL_XML_1_0 = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" diff --git a/signxml/signer.py b/signxml/signer.py index e1c03fb..d75f914 100644 --- a/signxml/signer.py +++ b/signxml/signer.py @@ -7,7 +7,7 @@ from cryptography.hazmat.primitives.hmac import HMAC from cryptography.hazmat.primitives.serialization import load_pem_private_key from lxml.etree import Element, SubElement, _Element -from OpenSSL.crypto import FILETYPE_PEM, dump_certificate +from OpenSSL.crypto import FILETYPE_PEM, X509, dump_certificate from .algorithms import ( CanonicalizationMethod, @@ -62,13 +62,14 @@ class XMLSigner(XMLSignatureProcessor): ``signxml.methods.enveloped``, ``signxml.methods.enveloping``, or ``signxml.methods.detached``. See :class:`SignatureConstructionMethod` for details. :param signature_algorithm: - Algorithm that will be used to generate the signature, composed of the signature algorithm and the digest - algorithm, separated by a hyphen. All algorithm IDs listed under the `Algorithm Identifiers and - Implementation Requirements `_ section of the XML Signature - 1.1 standard are supported. - :param digest_algorithm: Algorithm that will be used to hash the data during signature generation. All algorithm IDs - listed under the `Algorithm Identifiers and Implementation Requirements - `_ section of the XML Signature 1.1 standard are supported. + Algorithm that will be used to generate the signature. See :class:`SignatureMethod` for the list of algorithm + IDs supported. + :param digest_algorithm: + Algorithm that will be used to hash the data during signature generation. See :class:`DigestAlgorithm` for the + list of algorithm IDs supported. + :param c14n_algorithm: + Algorithm that will be used to canonicalize (serialize in a reproducible way) the XML that is signed. See + :class:`CanonicalizationMethod` for the list of algorithm IDs supported. """ signature_annotators: List @@ -92,7 +93,7 @@ def __init__( method: SignatureConstructionMethod = SignatureConstructionMethod.enveloped, signature_algorithm: Union[SignatureMethod, str] = SignatureMethod.RSA_SHA256, digest_algorithm: Union[DigestAlgorithm, str] = DigestAlgorithm.SHA256, - c14n_algorithm=CanonicalizationMethod.CANONICAL_XML_1_1, + c14n_algorithm: Union[CanonicalizationMethod, str] = CanonicalizationMethod.CANONICAL_XML_1_1, ): if method is None or method not in SignatureConstructionMethod: raise InvalidInput(f"Unknown signature construction method {method}") @@ -115,14 +116,14 @@ def sign( data, key=None, passphrase: Optional[bytes] = None, - cert=None, + cert: Optional[Union[str, List[str], List[X509]]] = None, reference_uri: Optional[Union[str, List[str], List[XMLSignatureReference]]] = None, key_name: Optional[str] = None, key_info: Optional[_Element] = None, id_attribute: Optional[str] = None, always_add_key_value: bool = False, inclusive_ns_prefixes: Optional[List[str]] = None, - signature_properties=None, + signature_properties: Optional[Union[_Element, List[_Element]]] = None, ) -> _Element: """ Sign the data and return the root element of the resulting XML tree. @@ -131,20 +132,19 @@ def sign( :type data: String, file-like object, or XML ElementTree Element API compatible object :param key: Key to be used for signing. When signing with a certificate or RSA/DSA/ECDSA key, this can be a string/bytes - containing a PEM-formatted key, or a :py:class:`cryptography.hazmat.primitives.interfaces.RSAPrivateKey`, - :py:class:`cryptography.hazmat.primitives.interfaces.DSAPrivateKey`, or - :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey` object. When signing with a + containing a PEM-formatted key, or a :class:`cryptography.hazmat.primitives.interfaces.RSAPrivateKey`, + :class:`cryptography.hazmat.primitives.interfaces.DSAPrivateKey`, or + :class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey` object. When signing with a HMAC, this should be a string containing the shared secret. :type key: - string, bytes, :py:class:`cryptography.hazmat.primitives.interfaces.RSAPrivateKey`, - :py:class:`cryptography.hazmat.primitives.interfaces.DSAPrivateKey`, or - :py:class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey` object + string, bytes, :class:`cryptography.hazmat.primitives.interfaces.RSAPrivateKey`, + :class:`cryptography.hazmat.primitives.interfaces.DSAPrivateKey`, or + :class:`cryptography.hazmat.primitives.interfaces.EllipticCurvePrivateKey` object :param passphrase: Passphrase to use to decrypt the key, if any. :param cert: X.509 certificate to use for signing. This should be a string containing a PEM-formatted certificate, or an - array of strings or OpenSSL.crypto.X509 objects containing the certificate and a chain of intermediate - certificates. - :type cert: string, array of strings, or array of OpenSSL.crypto.X509 objects + array of strings or :class:`OpenSSL.crypto.X509` objects containing the certificate and a chain of + intermediate certificates. :param reference_uri: Custom reference URI or list of reference URIs to incorporate into the signature. When ``method`` is set to ``detached`` or ``enveloped``, reference URIs are set to this value and only the referenced elements are @@ -175,10 +175,9 @@ def sign( :param signature_properties: One or more Elements that are to be included in the SignatureProperies section when using the detached method. - :type signature_properties: :py:class:`lxml.etree.Element` or list of :py:class:`lxml.etree.Element` s :returns: - A :py:class:`lxml.etree.Element` object representing the root of the XML tree containing the signature and + A :class:`lxml.etree._Element` object representing the root of the XML tree containing the signature and the payload data. To specify the location of an enveloped signature within **data**, insert a @@ -192,7 +191,7 @@ def sign( if isinstance(cert, (str, bytes)): cert_chain = list(iterate_pem(cert)) else: - cert_chain = cert + cert_chain = cert # type: ignore input_references = self._preprocess_reference_uri(reference_uri) diff --git a/signxml/verifier.py b/signxml/verifier.py index 1e23654..19ea040 100644 --- a/signxml/verifier.py +++ b/signxml/verifier.py @@ -242,7 +242,7 @@ def verify( :param parser: Custom XML parser instance to use when parsing **data**. The default parser arguments used by SignXML are: ``resolve_entities=False``. See https://lxml.de/FAQ.html#how-do-i-use-lxml-safely-as-a-web-service-endpoint. - :type parser: :py:class:`lxml.etree.XMLParser` compatible parser + :type parser: :class:`lxml.etree.XMLParser` compatible parser :param uri_resolver: Function to use to resolve reference URIs that don't start with "#". The function is called with a single string argument containing the URI to be resolved, and is expected to return a lxml.etree node or string. @@ -259,7 +259,7 @@ def verify( necessary to match the keys, and throws an InvalidInput error instead. Set this to True to bypass the error and validate the signature using X509Data only. - :raises: :py:class:`cryptography.exceptions.InvalidSignature` + :raises: :class:`signxml.exceptions.InvalidSignature` """ self.hmac_key = hmac_key self.require_x509 = require_x509