Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-signxml for openSUSE:Factory 
checked in at 2026-02-17 16:47:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-signxml (Old)
 and      /work/SRC/openSUSE:Factory/.python-signxml.new.1977 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-signxml"

Tue Feb 17 16:47:07 2026 rev:2 rq:1333380 version:4.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-signxml/python-signxml.changes    
2025-09-03 21:08:30.983387587 +0200
+++ /work/SRC/openSUSE:Factory/.python-signxml.new.1977/python-signxml.changes  
2026-02-17 16:47:35.864057753 +0100
@@ -1,0 +2,7 @@
+Mon Feb 16 15:06:38 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 4.3.0:
+  * Remove registration for ec.SECT* ECDSA curves
+  * Fix key info matching behavior
+
+-------------------------------------------------------------------

Old:
----
  signxml-4.2.0.tar.gz

New:
----
  signxml-4.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-signxml.spec ++++++
--- /var/tmp/diff_new_pack.lmsijK/_old  2026-02-17 16:47:40.356246240 +0100
+++ /var/tmp/diff_new_pack.lmsijK/_new  2026-02-17 16:47:40.360246408 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-signxml
 #
-# Copyright (c) 2025 SUSE LLC and contributors
+# Copyright (c) 2026 SUSE LLC and contributors
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           python-signxml
-Version:        4.2.0
+Version:        4.3.0
 Release:        0
 Summary:        Python XML Signature and XAdES library
 License:        Apache-2.0

++++++ signxml-4.2.0.tar.gz -> signxml-4.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/.github/workflows/ci.yml 
new/signxml-4.3.0/.github/workflows/ci.yml
--- old/signxml-4.2.0/.github/workflows/ci.yml  2020-02-02 01:00:00.000000000 
+0100
+++ new/signxml-4.3.0/.github/workflows/ci.yml  2020-02-02 01:00:00.000000000 
+0100
@@ -8,8 +8,8 @@
     strategy:
       max-parallel: 8
       matrix:
-        os: [ubuntu-22.04, ubuntu-latest, macos-13, macos-latest]
-        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
+        os: [ubuntu-22.04, ubuntu-latest, macos-14, macos-latest]
+        python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
     steps:
     - uses: actions/checkout@v4
     - uses: actions/setup-python@v5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/Changes.rst 
new/signxml-4.3.0/Changes.rst
--- old/signxml-4.2.0/Changes.rst       2020-02-02 01:00:00.000000000 +0100
+++ new/signxml-4.3.0/Changes.rst       2020-02-02 01:00:00.000000000 +0100
@@ -1,3 +1,22 @@
+Changes for v4.3.0 (2026-02-14)
+===============================
+
+- Remove registration for ec.SECT\* ECDSA curves (#289)
+
+- Fix key info matching behavior (#287)
+
+Changes for v4.2.2 (2026-01-21)
+===============================
+
+- Support default namespace with no xmlns=“” undeclarations in
+  sign/verify round-trip (#286)
+
+Changes for v4.2.1 (2026-01-18)
+===============================
+
+- Add legacy SigningCertificate with IssuerSerial for XAdES
+  interoperability (#282)
+
 Changes for v4.2.0 (2025-08-19)
 ===============================
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/PKG-INFO new/signxml-4.3.0/PKG-INFO
--- old/signxml-4.2.0/PKG-INFO  2020-02-02 01:00:00.000000000 +0100
+++ new/signxml-4.3.0/PKG-INFO  2020-02-02 01:00:00.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: signxml
-Version: 4.2.0
+Version: 4.3.0
 Summary: Python XML Signature and XAdES library
 Project-URL: Homepage, https://github.com/XML-Security/signxml
 Project-URL: Documentation, https://xml-security.github.io/signxml/
@@ -21,17 +21,16 @@
 Classifier: Operating System :: POSIX
 Classifier: Programming Language :: Python
 Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.8
-Classifier: Programming Language :: Python :: 3.9
 Classifier: Programming Language :: Python :: 3.10
 Classifier: Programming Language :: Python :: 3.11
 Classifier: Programming Language :: Python :: 3.12
 Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
 Classifier: Programming Language :: Python :: Implementation :: CPython
 Classifier: Programming Language :: Python :: Implementation :: PyPy
 Classifier: Topic :: Software Development
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
-Requires-Python: >=3.8
+Requires-Python: >=3.9
 Requires-Dist: certifi>=2023.11.17
 Requires-Dist: cryptography>=43
 Requires-Dist: lxml<7,>=5.2.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/pyproject.toml 
new/signxml-4.3.0/pyproject.toml
--- old/signxml-4.2.0/pyproject.toml    2020-02-02 01:00:00.000000000 +0100
+++ new/signxml-4.3.0/pyproject.toml    2020-02-02 01:00:00.000000000 +0100
@@ -2,7 +2,7 @@
 name = "signxml"
 description = "Python XML Signature and XAdES library"
 readme = "README.rst"
-requires-python = ">=3.8"
+requires-python = ">=3.9"
 license = { text = "Apache Software License" }
 authors = [{ name = "Andrey Kislyuk"}, {email = "[email protected]" }]
 maintainers = [{ name = "Andrey Kislyuk"}, {email = "[email protected]" }]
@@ -14,12 +14,11 @@
   "Operating System :: POSIX",
   "Programming Language :: Python",
   "Programming Language :: Python :: 3",
-  "Programming Language :: Python :: 3.8",
-  "Programming Language :: Python :: 3.9",
   "Programming Language :: Python :: 3.10",
   "Programming Language :: Python :: 3.11",
   "Programming Language :: Python :: 3.12",
   "Programming Language :: Python :: 3.13",
+  "Programming Language :: Python :: 3.14",
   "Programming Language :: Python :: Implementation :: CPython",
   "Programming Language :: Python :: Implementation :: PyPy",
   "Development Status :: 5 - Production/Stable",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/signxml/processor.py 
new/signxml-4.3.0/signxml/processor.py
--- old/signxml-4.2.0/signxml/processor.py      2020-02-02 01:00:00.000000000 
+0100
+++ new/signxml-4.3.0/signxml/processor.py      2020-02-02 01:00:00.000000000 
+0100
@@ -1,5 +1,7 @@
+import importlib.resources
 import logging
-import os
+import threading
+from functools import lru_cache
 from typing import Any, List, Tuple
 from xml.etree import ElementTree as stdlibElementTree
 
@@ -14,18 +16,24 @@
 logger = logging.getLogger(__name__)
 
 
+@lru_cache
+def get_schema(schema_file: str) -> etree.XMLSchema:
+    pkg_name = "signxml.xades.schemas" if schema_file.startswith("XAdES") else 
"signxml.schemas"
+    with importlib.resources.open_text(pkg_name, schema_file) as schema_fh:
+        return etree.XMLSchema(etree.parse(schema_fh))
+
+
 class XMLProcessor:
     _schemas: List[Any] = []
     schema_files: List[Any] = []
     _default_parser, _parser = None, None
-    _schema_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), 
"schemas"))
 
     @classmethod
     def schemas(cls):
-        if len(cls._schemas) == 0:
-            for schema_file in cls.schema_files:
-                schema_path = os.path.join(cls._schema_dir, schema_file)
-                cls._schemas.append(etree.XMLSchema(etree.parse(schema_path)))
+        with threading.Lock():
+            if len(cls._schemas) == 0:
+                for schema_file in cls.schema_files:
+                    cls._schemas.append(get_schema(schema_file))
         return cls._schemas
 
     @property
@@ -62,19 +70,22 @@
     schema_files = ["xmldsig1-schema.xsd"]
 
     # See https://tools.ietf.org/html/rfc5656
+    # ec.SECT* curves have been de-registered due to CVE-2026-26007
+    # 
(https://github.com/pyca/cryptography/security/advisories/GHSA-r6ph-v2qm-q3c2)
+    # List of disabled curves:
+    # "urn:oid:1.3.132.0.1": ec.SECT163K1,
+    # "urn:oid:1.3.132.0.26": ec.SECT233K1,
+    # "urn:oid:1.3.132.0.27": ec.SECT233R1,
+    # "urn:oid:1.3.132.0.16": ec.SECT283R1,
+    # "urn:oid:1.3.132.0.36": ec.SECT409K1,
+    # "urn:oid:1.3.132.0.37": ec.SECT409R1,
+    # "urn:oid:1.3.132.0.38": ec.SECT571K1
     known_ecdsa_curves = {
         "urn:oid:1.2.840.10045.3.1.7": ec.SECP256R1,
         "urn:oid:1.3.132.0.34": ec.SECP384R1,
         "urn:oid:1.3.132.0.35": ec.SECP521R1,
-        "urn:oid:1.3.132.0.1": ec.SECT163K1,
         "urn:oid:1.2.840.10045.3.1.1": ec.SECP192R1,
         "urn:oid:1.3.132.0.33": ec.SECP224R1,
-        "urn:oid:1.3.132.0.26": ec.SECT233K1,
-        "urn:oid:1.3.132.0.27": ec.SECT233R1,
-        "urn:oid:1.3.132.0.16": ec.SECT283R1,
-        "urn:oid:1.3.132.0.36": ec.SECT409K1,
-        "urn:oid:1.3.132.0.37": ec.SECT409R1,
-        "urn:oid:1.3.132.0.38": ec.SECT571K1,
     }
     known_ecdsa_curve_oids = {ec().name: oid for oid, ec in 
known_ecdsa_curves.items()}  # type: ignore[abstract]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/signxml/signer.py 
new/signxml-4.3.0/signxml/signer.py
--- old/signxml-4.2.0/signxml/signer.py 2020-02-02 01:00:00.000000000 +0100
+++ new/signxml-4.3.0/signxml/signer.py 2020-02-02 01:00:00.000000000 +0100
@@ -7,7 +7,7 @@
 from cryptography.hazmat.primitives.asymmetric.padding import MGF1, PSS, 
PKCS1v15
 from cryptography.hazmat.primitives.hmac import HMAC
 from cryptography.hazmat.primitives.serialization import Encoding, 
load_pem_private_key
-from lxml.etree import Element, SubElement, _Element
+from lxml.etree import Element, QName, SubElement, _Element
 
 from .algorithms import (
     CanonicalizationMethod,
@@ -117,6 +117,20 @@
         self._parser = None
         self.signature_annotators = [self._add_key_info]
 
+    def _ds_tag(self, tag):
+        """
+        Create a QName for the ds namespace, respecting the configured 
namespace mapping.
+
+        When the default namespace is set to the ds namespace ({None: 
namespaces.ds}),
+        elements should be created without an explicit namespace so they 
inherit from
+        the nsmap context. This avoids spurious xmlns="" undeclarations in 
C14N output.
+
+        See https://github.com/XML-Security/signxml/issues/275
+        """
+        if None in self.namespaces and self.namespaces[None] == namespaces.ds: 
 # type:ignore[index]
+            return QName(None, tag)
+        return ds_tag(tag)
+
     def check_deprecated_methods(self):
         if "SHA1" in self.sign_alg.name or "SHA1" in self.digest_alg.name:
             msg = "SHA1-based algorithms are not supported in the default 
configuration because they are not secure"
@@ -304,9 +318,9 @@
         if self.sign_alg.name.startswith("HMAC_"):
             return
         if signing_settings.key_info is None:
-            key_info = SubElement(sig_root, ds_tag("KeyInfo"))
+            key_info = SubElement(sig_root, self._ds_tag("KeyInfo"))
             if signing_settings.key_name is not None:
-                keyname = SubElement(key_info, ds_tag("KeyName"))
+                keyname = SubElement(key_info, self._ds_tag("KeyName"))
                 keyname.text = signing_settings.key_name
 
             if signing_settings.cert_chain is None or 
signing_settings.always_add_key_value:
@@ -314,9 +328,9 @@
 
             if signing_settings.cert_chain is not None:
                 assert len(signing_settings.cert_chain) > 0
-                x509_data = SubElement(key_info, ds_tag("X509Data"))
+                x509_data = SubElement(key_info, self._ds_tag("X509Data"))
                 for cert in signing_settings.cert_chain:
-                    x509_certificate = SubElement(x509_data, 
ds_tag("X509Certificate"))
+                    x509_certificate = SubElement(x509_data, 
self._ds_tag("X509Certificate"))
                     if isinstance(cert, (str, bytes)):
                         x509_certificate.text = strip_pem_header(cert)
                     else:
@@ -333,7 +347,7 @@
         return c14n_inputs, new_references
 
     def _unpack(self, data, references: List[SignatureReference]):
-        sig_root = Element(ds_tag("Signature"), nsmap=self.namespaces)
+        sig_root = Element(self._ds_tag("Signature"), nsmap=self.namespaces)
         if self.construction_method == SignatureConstructionMethod.enveloped:
             if isinstance(data, (str, bytes)):
                 raise InvalidInput("When using enveloped signature, **data** 
must be an XML element")
@@ -376,7 +390,7 @@
                 c14n_inputs = [self.get_root(data)]
         elif self.construction_method == 
SignatureConstructionMethod.enveloping:
             doc_root = sig_root
-            c14n_inputs = [Element(ds_tag("Object"), nsmap=self.namespaces, 
Id="object")]
+            c14n_inputs = [Element(self._ds_tag("Object"), 
nsmap=self.namespaces, Id="object")]
             if isinstance(data, (str, bytes)):
                 c14n_inputs[0].text = data
             else:
@@ -389,14 +403,16 @@
     ):
         assert reference.c14n_method is not None
         if self.construction_method == SignatureConstructionMethod.enveloped:
-            SubElement(transforms_node, ds_tag("Transform"), 
Algorithm=SignatureConstructionMethod.enveloped.value)
+            SubElement(
+                transforms_node, self._ds_tag("Transform"), 
Algorithm=SignatureConstructionMethod.enveloped.value
+            )
             if not exclude_c14n_transform_element:
-                SubElement(transforms_node, ds_tag("Transform"), 
Algorithm=reference.c14n_method.value)
+                SubElement(transforms_node, self._ds_tag("Transform"), 
Algorithm=reference.c14n_method.value)
         else:
             if not exclude_c14n_transform_element:
                 c14n_xform = SubElement(
                     transforms_node,
-                    ds_tag("Transform"),
+                    self._ds_tag("Transform"),
                     Algorithm=reference.c14n_method.value,
                 )
             if reference.inclusive_ns_prefixes:
@@ -407,41 +423,41 @@
     def _build_sig(
         self, sig_root, references, c14n_inputs, inclusive_ns_prefixes, 
exclude_c14n_transform_element=False
     ):
-        signed_info = SubElement(sig_root, ds_tag("SignedInfo"), 
nsmap=self.namespaces)
-        sig_c14n_method = SubElement(signed_info, 
ds_tag("CanonicalizationMethod"), Algorithm=self.c14n_alg.value)
+        signed_info = SubElement(sig_root, self._ds_tag("SignedInfo"), 
nsmap=self.namespaces)
+        sig_c14n_method = SubElement(signed_info, 
self._ds_tag("CanonicalizationMethod"), Algorithm=self.c14n_alg.value)
         if inclusive_ns_prefixes:
             SubElement(sig_c14n_method, ec_tag("InclusiveNamespaces"), 
PrefixList=" ".join(inclusive_ns_prefixes))
 
-        SubElement(signed_info, ds_tag("SignatureMethod"), 
Algorithm=self.sign_alg.value)
+        SubElement(signed_info, self._ds_tag("SignatureMethod"), 
Algorithm=self.sign_alg.value)
         for i, reference in enumerate(references):
             if reference.c14n_method is None:
                 reference = replace(reference, c14n_method=self.c14n_alg)
             if reference.inclusive_ns_prefixes is None:
                 reference = replace(reference, 
inclusive_ns_prefixes=inclusive_ns_prefixes)
-            reference_node = SubElement(signed_info, ds_tag("Reference"), 
URI=reference.URI)
-            transforms = SubElement(reference_node, ds_tag("Transforms"))
+            reference_node = SubElement(signed_info, 
self._ds_tag("Reference"), URI=reference.URI)
+            transforms = SubElement(reference_node, self._ds_tag("Transforms"))
             self._build_transforms_for_reference(
                 transforms_node=transforms,
                 reference=reference,
                 exclude_c14n_transform_element=exclude_c14n_transform_element,
             )
-            SubElement(reference_node, ds_tag("DigestMethod"), 
Algorithm=self.digest_alg.value)
-            digest_value = SubElement(reference_node, ds_tag("DigestValue"))
+            SubElement(reference_node, self._ds_tag("DigestMethod"), 
Algorithm=self.digest_alg.value)
+            digest_value = SubElement(reference_node, 
self._ds_tag("DigestValue"))
             payload_c14n = self._c14n(
                 c14n_inputs[i], algorithm=reference.c14n_method, 
inclusive_ns_prefixes=reference.inclusive_ns_prefixes
             )
             digest = self._get_digest(payload_c14n, algorithm=self.digest_alg)
             digest_value.text = b64encode(digest).decode()
-        signature_value = SubElement(sig_root, ds_tag("SignatureValue"))
+        signature_value = SubElement(sig_root, self._ds_tag("SignatureValue"))
         return signed_info, signature_value
 
     def _build_signature_properties(self, signature_properties):
         # FIXME: make this use the annotator API
-        obj = Element(ds_tag("Object"), attrib={"Id": "prop"}, 
nsmap=self.namespaces)
-        signature_properties_el = Element(ds_tag("SignatureProperties"))
+        obj = Element(self._ds_tag("Object"), attrib={"Id": "prop"}, 
nsmap=self.namespaces)
+        signature_properties_el = Element(self._ds_tag("SignatureProperties"))
         for i, el in enumerate(signature_properties):
             signature_property = Element(
-                ds_tag("SignatureProperty"),
+                self._ds_tag("SignatureProperty"),
                 attrib={
                     "Id": el.attrib.pop("Id", f"sigprop{i}"),
                     "Target": el.attrib.pop("Target", f"#sigproptarget{i}"),
@@ -456,17 +472,17 @@
         """
         Add the public components of the key to the signature (see 
https://www.w3.org/TR/xmldsig-core2/#sec-KeyValue).
         """
-        key_value = SubElement(key_info_node, ds_tag("KeyValue"))
+        key_value = SubElement(key_info_node, self._ds_tag("KeyValue"))
         if self.sign_alg.name.startswith("RSA_") or 
self.sign_alg.name.startswith("SHA"):
-            rsa_key_value = SubElement(key_value, ds_tag("RSAKeyValue"))
-            modulus = SubElement(rsa_key_value, ds_tag("Modulus"))
+            rsa_key_value = SubElement(key_value, self._ds_tag("RSAKeyValue"))
+            modulus = SubElement(rsa_key_value, self._ds_tag("Modulus"))
             modulus.text = 
b64encode(long_to_bytes(key.public_key().public_numbers().n)).decode()
-            exponent = SubElement(rsa_key_value, ds_tag("Exponent"))
+            exponent = SubElement(rsa_key_value, self._ds_tag("Exponent"))
             exponent.text = 
b64encode(long_to_bytes(key.public_key().public_numbers().e)).decode()
         elif self.sign_alg.name.startswith("DSA_"):
-            dsa_key_value = SubElement(key_value, ds_tag("DSAKeyValue"))
+            dsa_key_value = SubElement(key_value, self._ds_tag("DSAKeyValue"))
             for field in "p", "q", "g", "y":
-                e = SubElement(dsa_key_value, ds_tag(field.upper()))
+                e = SubElement(dsa_key_value, self._ds_tag(field.upper()))
 
                 if field == "y":
                     key_params = key.public_key().public_numbers()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/signxml/verifier.py 
new/signxml-4.3.0/signxml/verifier.py
--- old/signxml-4.2.0/signxml/verifier.py       2020-02-02 01:00:00.000000000 
+0100
+++ new/signxml-4.3.0/signxml/verifier.py       2020-02-02 01:00:00.000000000 
+0100
@@ -82,9 +82,9 @@
     Ignore the presence of a KeyValue element when X509Data is present in the 
signature and used for verifying.
     The presence of both elements is an ambiguity and a security hazard. The 
public key used to sign the
     document is already encoded in the certificate (which is in X509Data), so 
the verifier must either ignore
-    KeyValue or make sure it matches what's in the certificate. SignXML does 
not implement the functionality
-    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.
+    KeyValue or make sure it matches what's in the certificate. When set to 
``False``, SignXML compares KeyValue
+    (and DEREncodedKeyValue) against the X.509 certificate and raises 
InvalidInput on mismatch. Set this to
+    ``True`` to bypass the check and validate the signature using X509Data 
only.
     """
 
     default_reference_c14n_method: CanonicalizationMethod = 
CanonicalizationMethod.CANONICAL_XML_1_1
@@ -261,7 +261,7 @@
         return X509CertChainVerifier(ca_pem_file=ca_pem_file)
 
     def _match_key_values(self, key_value, der_encoded_key_value, 
signing_cert, signature_alg):
-        if self.config.ignore_ambiguous_key_info is False:
+        if self.config.ignore_ambiguous_key_info is True:
             return
         cert_pub_key = signing_cert.public_key()
         # If both X509Data and KeyValue are present, match one against the 
other and raise an error on mismatch
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/signxml/xades/xades.py 
new/signxml-4.3.0/signxml/xades/xades.py
--- old/signxml-4.2.0/signxml/xades/xades.py    2020-02-02 01:00:00.000000000 
+0100
+++ new/signxml-4.3.0/signxml/xades/xades.py    2020-02-02 01:00:00.000000000 
+0100
@@ -98,6 +98,8 @@
         Parameters to pass to the :class:`signxml.XMLSigner` constructor.
     """
 
+    use_deprecated_legacy_signing_certificate: bool = False
+
     def __init__(
         self,
         signature_policy: Optional[XAdESSignaturePolicy] = None,
@@ -190,7 +192,7 @@
         signing_time.text = 
datetime.datetime.now(datetime.timezone.utc).isoformat(timespec="seconds")
 
     def add_signing_certificate(self, signed_signature_properties, sig_root, 
signing_settings: SigningSettings):
-        # TODO: check if we need to support SigningCertificate
+        # Emit both legacy SigningCertificate (SHA1 + IssuerSerial) and 
SigningCertificateV2
         signing_cert_v2 = SubElement(
             signed_signature_properties, xades_tag("SigningCertificateV2"), 
nsmap=self.namespaces
         )
@@ -202,6 +204,33 @@
                 loaded_cert = 
x509.load_pem_x509_certificate(add_pem_header(cert))
             der_encoded_cert = loaded_cert.public_bytes(Encoding.DER)
             cert_digest_bytes = self._get_digest(der_encoded_cert, 
algorithm=self.digest_alg)
+
+            # Legacy SigningCertificate
+            if self.use_deprecated_legacy_signing_certificate:
+                cert_digest_sha1_bytes = self._get_digest(der_encoded_cert, 
algorithm=DigestAlgorithm.SHA1)
+                signing_cert = SubElement(
+                    signed_signature_properties, 
xades_tag("SigningCertificate"), nsmap=self.namespaces
+                )
+
+                cert_node_legacy = SubElement(signing_cert, xades_tag("Cert"), 
nsmap=self.namespaces)
+                cert_digest = SubElement(cert_node_legacy, 
xades_tag("CertDigest"), nsmap=self.namespaces)
+                SubElement(
+                    cert_digest, ds_tag("DigestMethod"), 
nsmap=self.namespaces, Algorithm=DigestAlgorithm.SHA1.value
+                )
+                digest_value_node = SubElement(cert_digest, 
ds_tag("DigestValue"), nsmap=self.namespaces)
+                digest_value_node.text = 
b64encode(cert_digest_sha1_bytes).decode()
+                issuer_serial = SubElement(cert_node_legacy, 
xades_tag("IssuerSerial"), nsmap=self.namespaces)
+                issuer_name = SubElement(issuer_serial, 
ds_tag("X509IssuerName"), nsmap=self.namespaces)
+                issuer_name.text = "C={C},O={O},OU={OU},CN={CN}".format(  # 
type:ignore[str-bytes-safe]
+                    
C=loaded_cert.issuer.get_attributes_for_oid(x509.oid.NameOID.COUNTRY_NAME)[0].value,
+                    
O=loaded_cert.issuer.get_attributes_for_oid(x509.oid.NameOID.ORGANIZATION_NAME)[0].value,
+                    
OU=loaded_cert.issuer.get_attributes_for_oid(x509.oid.NameOID.ORGANIZATIONAL_UNIT_NAME)[0].value,
+                    
CN=loaded_cert.issuer.get_attributes_for_oid(x509.oid.NameOID.COMMON_NAME)[0].value,
+                )
+                serial_number = SubElement(issuer_serial, 
ds_tag("X509SerialNumber"), nsmap=self.namespaces)
+                serial_number.text = str(loaded_cert.serial_number)
+
+            # SigningCertificateV2 (current default)
             cert_node = SubElement(signing_cert_v2, xades_tag("Cert"), 
nsmap=self.namespaces)
             cert_digest = SubElement(cert_node, xades_tag("CertDigest"), 
nsmap=self.namespaces)
             SubElement(cert_digest, ds_tag("DigestMethod"), 
nsmap=self.namespaces, Algorithm=self.digest_alg.value)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/signxml-4.2.0/test/test.py 
new/signxml-4.3.0/test/test.py
--- old/signxml-4.2.0/test/test.py      2020-02-02 01:00:00.000000000 +0100
+++ new/signxml-4.3.0/test/test.py      2020-02-02 01:00:00.000000000 +0100
@@ -479,6 +479,29 @@
         expected_match = f'<Signature xmlns="{namespaces["ds"]}">'
         self.assertTrue(re.search(expected_match.encode("ascii"), signed_data))
 
+    def test_sign_verify_default_ns_roundtrip(self):
+        """
+        Test sign/verify round-trip when using default namespace.
+
+        See https://github.com/XML-Security/signxml/issues/275
+        """
+        crt, key = self.load_example_keys()
+        data = etree.parse(self.example_xml_files[0]).getroot()
+        signer = XMLSigner()
+        signer.namespaces = {None: namespaces["ds"]}
+        signed = signer.sign(data, key=key, cert=crt)
+
+        signed_info = signed.find(".//SignedInfo")
+        self.assertIsNotNone(signed_info)
+        c14n_output = etree.tostring(signed_info, method="c14n").decode()
+        self.assertNotIn('xmlns=""', c14n_output)
+        self.assertIn('xmlns="http://www.w3.org/2000/09/xmldsig#";', 
c14n_output)
+
+        signed_data = etree.tostring(signed)
+        verifier = XMLVerifier()
+        verifier.excise_empty_xmlns_declarations = True
+        verifier.verify(signed_data, x509_cert=crt)
+
     def test_elementtree_compat(self):
         data = stdlibElementTree.parse(self.example_xml_files[0]).getroot()
         signer = XMLSigner()
@@ -625,6 +648,26 @@
     def test_excision_of_untrusted_comments(self):
         pass  # TODO: test comments excision
 
+    def test_mismatched_key_value_with_x509_data(self):
+        crt, key = self.load_example_keys()
+        data = etree.parse(os.path.join(os.path.dirname(__file__), 
"example.xml")).getroot()
+        signer = XMLSigner()
+        signed = signer.sign(data, key=key, cert=crt, 
always_add_key_value=True)
+        key_info = signed.find(".//ds:KeyInfo", namespaces=namespaces)
+        key_value = key_info.find("ds:KeyValue", namespaces=namespaces)
+        key_info.remove(key_value)
+        mismatched_key = rsa.generate_private_key(public_exponent=65537, 
key_size=2048)
+        signer._serialize_key_value(mismatched_key, key_info)
+        signed_xml = etree.tostring(signed)
+
+        with self.assertRaisesRegex(
+            InvalidInput,
+            "Both X509Data and KeyValue found and they represent different 
public keys",
+        ):
+            XMLVerifier().verify(signed_xml, x509_cert=crt)
+
+        XMLVerifier().verify(signed_xml, x509_cert=crt, 
ignore_ambiguous_key_info=True)
+
     def test_ws_security(self):
         wsse_dir = os.path.join(interop_dir, "ws-security", "ws.js")
         with open(os.path.join(wsse_dir, "examples", "server_public.pem"), 
"rb") as fh:

Reply via email to