Skip to content

Commit 0399496

Browse files
authored
Revert "fix: respect namespace config for default namespace C14N" (#285)
Reverts #283
1 parent 65eeb0e commit 0399496

2 files changed

Lines changed: 27 additions & 68 deletions

File tree

signxml/signer.py

Lines changed: 27 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from cryptography.hazmat.primitives.asymmetric.padding import MGF1, PSS, PKCS1v15
88
from cryptography.hazmat.primitives.hmac import HMAC
99
from cryptography.hazmat.primitives.serialization import Encoding, load_pem_private_key
10-
from lxml.etree import Element, QName, SubElement, _Element
10+
from lxml.etree import Element, SubElement, _Element
1111

1212
from .algorithms import (
1313
CanonicalizationMethod,
@@ -117,20 +117,6 @@ def __init__(
117117
self._parser = None
118118
self.signature_annotators = [self._add_key_info]
119119

120-
def _ds_tag(self, tag):
121-
"""
122-
Create a QName for the ds namespace, respecting the configured namespace mapping.
123-
124-
When the default namespace is set to the ds namespace ({None: namespaces.ds}),
125-
elements should be created without an explicit namespace so they inherit from
126-
the nsmap context. This avoids spurious xmlns="" undeclarations in C14N output.
127-
128-
See https://github.com/XML-Security/signxml/issues/275
129-
"""
130-
if None in self.namespaces and self.namespaces[None] == namespaces.ds:
131-
return QName(None, tag)
132-
return ds_tag(tag)
133-
134120
def check_deprecated_methods(self):
135121
if "SHA1" in self.sign_alg.name or "SHA1" in self.digest_alg.name:
136122
msg = "SHA1-based algorithms are not supported in the default configuration because they are not secure"
@@ -318,19 +304,19 @@ def _add_key_info(self, sig_root, signing_settings: SigningSettings):
318304
if self.sign_alg.name.startswith("HMAC_"):
319305
return
320306
if signing_settings.key_info is None:
321-
key_info = SubElement(sig_root, self._ds_tag("KeyInfo"))
307+
key_info = SubElement(sig_root, ds_tag("KeyInfo"))
322308
if signing_settings.key_name is not None:
323-
keyname = SubElement(key_info, self._ds_tag("KeyName"))
309+
keyname = SubElement(key_info, ds_tag("KeyName"))
324310
keyname.text = signing_settings.key_name
325311

326312
if signing_settings.cert_chain is None or signing_settings.always_add_key_value:
327313
self._serialize_key_value(signing_settings.key, key_info)
328314

329315
if signing_settings.cert_chain is not None:
330316
assert len(signing_settings.cert_chain) > 0
331-
x509_data = SubElement(key_info, self._ds_tag("X509Data"))
317+
x509_data = SubElement(key_info, ds_tag("X509Data"))
332318
for cert in signing_settings.cert_chain:
333-
x509_certificate = SubElement(x509_data, self._ds_tag("X509Certificate"))
319+
x509_certificate = SubElement(x509_data, ds_tag("X509Certificate"))
334320
if isinstance(cert, (str, bytes)):
335321
x509_certificate.text = strip_pem_header(cert)
336322
else:
@@ -347,7 +333,7 @@ def _get_c14n_inputs_from_references(self, doc_root, references: List[SignatureR
347333
return c14n_inputs, new_references
348334

349335
def _unpack(self, data, references: List[SignatureReference]):
350-
sig_root = Element(self._ds_tag("Signature"), nsmap=self.namespaces)
336+
sig_root = Element(ds_tag("Signature"), nsmap=self.namespaces)
351337
if self.construction_method == SignatureConstructionMethod.enveloped:
352338
if isinstance(data, (str, bytes)):
353339
raise InvalidInput("When using enveloped signature, **data** must be an XML element")
@@ -390,7 +376,7 @@ def _unpack(self, data, references: List[SignatureReference]):
390376
c14n_inputs = [self.get_root(data)]
391377
elif self.construction_method == SignatureConstructionMethod.enveloping:
392378
doc_root = sig_root
393-
c14n_inputs = [Element(self._ds_tag("Object"), nsmap=self.namespaces, Id="object")]
379+
c14n_inputs = [Element(ds_tag("Object"), nsmap=self.namespaces, Id="object")]
394380
if isinstance(data, (str, bytes)):
395381
c14n_inputs[0].text = data
396382
else:
@@ -403,14 +389,14 @@ def _build_transforms_for_reference(
403389
):
404390
assert reference.c14n_method is not None
405391
if self.construction_method == SignatureConstructionMethod.enveloped:
406-
SubElement(transforms_node, self._ds_tag("Transform"), Algorithm=SignatureConstructionMethod.enveloped.value)
392+
SubElement(transforms_node, ds_tag("Transform"), Algorithm=SignatureConstructionMethod.enveloped.value)
407393
if not exclude_c14n_transform_element:
408-
SubElement(transforms_node, self._ds_tag("Transform"), Algorithm=reference.c14n_method.value)
394+
SubElement(transforms_node, ds_tag("Transform"), Algorithm=reference.c14n_method.value)
409395
else:
410396
if not exclude_c14n_transform_element:
411397
c14n_xform = SubElement(
412398
transforms_node,
413-
self._ds_tag("Transform"),
399+
ds_tag("Transform"),
414400
Algorithm=reference.c14n_method.value,
415401
)
416402
if reference.inclusive_ns_prefixes:
@@ -421,41 +407,41 @@ def _build_transforms_for_reference(
421407
def _build_sig(
422408
self, sig_root, references, c14n_inputs, inclusive_ns_prefixes, exclude_c14n_transform_element=False
423409
):
424-
signed_info = SubElement(sig_root, self._ds_tag("SignedInfo"), nsmap=self.namespaces)
425-
sig_c14n_method = SubElement(signed_info, self._ds_tag("CanonicalizationMethod"), Algorithm=self.c14n_alg.value)
410+
signed_info = SubElement(sig_root, ds_tag("SignedInfo"), nsmap=self.namespaces)
411+
sig_c14n_method = SubElement(signed_info, ds_tag("CanonicalizationMethod"), Algorithm=self.c14n_alg.value)
426412
if inclusive_ns_prefixes:
427413
SubElement(sig_c14n_method, ec_tag("InclusiveNamespaces"), PrefixList=" ".join(inclusive_ns_prefixes))
428414

429-
SubElement(signed_info, self._ds_tag("SignatureMethod"), Algorithm=self.sign_alg.value)
415+
SubElement(signed_info, ds_tag("SignatureMethod"), Algorithm=self.sign_alg.value)
430416
for i, reference in enumerate(references):
431417
if reference.c14n_method is None:
432418
reference = replace(reference, c14n_method=self.c14n_alg)
433419
if reference.inclusive_ns_prefixes is None:
434420
reference = replace(reference, inclusive_ns_prefixes=inclusive_ns_prefixes)
435-
reference_node = SubElement(signed_info, self._ds_tag("Reference"), URI=reference.URI)
436-
transforms = SubElement(reference_node, self._ds_tag("Transforms"))
421+
reference_node = SubElement(signed_info, ds_tag("Reference"), URI=reference.URI)
422+
transforms = SubElement(reference_node, ds_tag("Transforms"))
437423
self._build_transforms_for_reference(
438424
transforms_node=transforms,
439425
reference=reference,
440426
exclude_c14n_transform_element=exclude_c14n_transform_element,
441427
)
442-
SubElement(reference_node, self._ds_tag("DigestMethod"), Algorithm=self.digest_alg.value)
443-
digest_value = SubElement(reference_node, self._ds_tag("DigestValue"))
428+
SubElement(reference_node, ds_tag("DigestMethod"), Algorithm=self.digest_alg.value)
429+
digest_value = SubElement(reference_node, ds_tag("DigestValue"))
444430
payload_c14n = self._c14n(
445431
c14n_inputs[i], algorithm=reference.c14n_method, inclusive_ns_prefixes=reference.inclusive_ns_prefixes
446432
)
447433
digest = self._get_digest(payload_c14n, algorithm=self.digest_alg)
448434
digest_value.text = b64encode(digest).decode()
449-
signature_value = SubElement(sig_root, self._ds_tag("SignatureValue"))
435+
signature_value = SubElement(sig_root, ds_tag("SignatureValue"))
450436
return signed_info, signature_value
451437

452438
def _build_signature_properties(self, signature_properties):
453439
# FIXME: make this use the annotator API
454-
obj = Element(self._ds_tag("Object"), attrib={"Id": "prop"}, nsmap=self.namespaces)
455-
signature_properties_el = Element(self._ds_tag("SignatureProperties"))
440+
obj = Element(ds_tag("Object"), attrib={"Id": "prop"}, nsmap=self.namespaces)
441+
signature_properties_el = Element(ds_tag("SignatureProperties"))
456442
for i, el in enumerate(signature_properties):
457443
signature_property = Element(
458-
self._ds_tag("SignatureProperty"),
444+
ds_tag("SignatureProperty"),
459445
attrib={
460446
"Id": el.attrib.pop("Id", f"sigprop{i}"),
461447
"Target": el.attrib.pop("Target", f"#sigproptarget{i}"),
@@ -470,17 +456,17 @@ def _serialize_key_value(self, key, key_info_node):
470456
"""
471457
Add the public components of the key to the signature (see https://www.w3.org/TR/xmldsig-core2/#sec-KeyValue).
472458
"""
473-
key_value = SubElement(key_info_node, self._ds_tag("KeyValue"))
459+
key_value = SubElement(key_info_node, ds_tag("KeyValue"))
474460
if self.sign_alg.name.startswith("RSA_") or self.sign_alg.name.startswith("SHA"):
475-
rsa_key_value = SubElement(key_value, self._ds_tag("RSAKeyValue"))
476-
modulus = SubElement(rsa_key_value, self._ds_tag("Modulus"))
461+
rsa_key_value = SubElement(key_value, ds_tag("RSAKeyValue"))
462+
modulus = SubElement(rsa_key_value, ds_tag("Modulus"))
477463
modulus.text = b64encode(long_to_bytes(key.public_key().public_numbers().n)).decode()
478-
exponent = SubElement(rsa_key_value, self._ds_tag("Exponent"))
464+
exponent = SubElement(rsa_key_value, ds_tag("Exponent"))
479465
exponent.text = b64encode(long_to_bytes(key.public_key().public_numbers().e)).decode()
480466
elif self.sign_alg.name.startswith("DSA_"):
481-
dsa_key_value = SubElement(key_value, self._ds_tag("DSAKeyValue"))
467+
dsa_key_value = SubElement(key_value, ds_tag("DSAKeyValue"))
482468
for field in "p", "q", "g", "y":
483-
e = SubElement(dsa_key_value, self._ds_tag(field.upper()))
469+
e = SubElement(dsa_key_value, ds_tag(field.upper()))
484470

485471
if field == "y":
486472
key_params = key.public_key().public_numbers()

test/test.py

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -479,33 +479,6 @@ def test_changing_signature_namespace_prefix_to_default(self):
479479
expected_match = f'<Signature xmlns="{namespaces["ds"]}">'
480480
self.assertTrue(re.search(expected_match.encode("ascii"), signed_data))
481481

482-
def test_default_namespace_c14n_no_xmlns_undeclarations(self):
483-
"""
484-
Test that using default namespace doesn't produce xmlns="" undeclarations in C14N.
485-
486-
When signer.namespaces = {None: ds_namespace}, the SignedInfo C14N should not
487-
contain xmlns="" on child elements. This regression was reported in issue #275.
488-
"""
489-
data = etree.parse(self.example_xml_files[0]).getroot()
490-
signer = XMLSigner()
491-
signer.namespaces = {None: namespaces["ds"]}
492-
signed = signer.sign(data, key=self.keys["rsa"])
493-
494-
# Verify signature round-trips correctly
495-
XMLVerifier().verify(signed, x509_cert=self.certs["example"])
496-
497-
# Extract SignedInfo and canonicalize it
498-
signed_info = signed.find(".//{http://www.w3.org/2000/09/xmldsig#}SignedInfo")
499-
c14n_output = etree.tostring(signed_info, method="c14n").decode()
500-
501-
# SignedInfo should have xmlns declaration, but child elements should NOT have xmlns=""
502-
# Count occurrences: should be exactly 1 (on SignedInfo itself)
503-
xmlns_count = c14n_output.count('xmlns="')
504-
self.assertEqual(xmlns_count, 1, f"Expected 1 xmlns declaration, found {xmlns_count}. C14N output: {c14n_output}")
505-
506-
# Specifically verify no xmlns="" undeclarations
507-
self.assertNotIn('xmlns=""', c14n_output, f"Found xmlns='' undeclaration in C14N output: {c14n_output}")
508-
509482
def test_elementtree_compat(self):
510483
data = stdlibElementTree.parse(self.example_xml_files[0]).getroot()
511484
signer = XMLSigner()

0 commit comments

Comments
 (0)