Skip to content

Commit 01e783e

Browse files
authored
Fix ECDSA signature encoding/decoding (#150)
According to https://www.w3.org/TR/xmldsig-core1/#sec-ECDSA, "The signature value consists of the base64 encoding of the concatenation of two octet-streams that respectively result from the octet-encoding of the values r and s in that order." Previously, the signature value was assumed to be a DER-encoded signature, which is incorrect. There was already code for dealing with a similiar situation for DSA signatures, but not ECDSA.
1 parent a76018e commit 01e783e

File tree

1 file changed

+31
-4
lines changed

1 file changed

+31
-4
lines changed

signxml/__init__.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from lxml import etree
88
from lxml.etree import Element, SubElement
99

10-
from cryptography.hazmat.primitives.asymmetric import rsa, dsa, ec
10+
from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa, utils
1111
from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
1212
from cryptography.hazmat.primitives.hashes import Hash, SHA1, SHA224, SHA256, SHA384, SHA512
1313
from cryptography.hazmat.backends import default_backend
@@ -386,6 +386,12 @@ def sign(self, data, key=None, passphrase=None, cert=None, reference_uri=None, k
386386
r = decoded_signature['r']
387387
s = decoded_signature['s']
388388
signature = long_to_bytes(r).rjust(32, b"\0") + long_to_bytes(s).rjust(32, b"\0")
389+
elif self.sign_alg.startswith("ecdsa-"):
390+
# Note: The output of the ECDSA signer is a DER-encoded ASN.1 sequence of two DER integers.
391+
(r, s) = utils.decode_dss_signature(signature)
392+
int_len = key.key_size // 8
393+
signature = long_to_bytes(r, blocksize=int_len)
394+
signature += long_to_bytes(s, blocksize=int_len)
389395

390396
signature_value_element.text = ensure_str(b64encode(signature))
391397

@@ -553,9 +559,14 @@ def _verify_signature_with_pubkey(self, signed_info_c14n, raw_signature, key_val
553559
y = bytes_to_long(key_data[len(key_data)//2:])
554560
curve_class = self.known_ecdsa_curves[named_curve.get("URI")]
555561
key = ec.EllipticCurvePublicNumbers(x=x, y=y, curve=curve_class()).public_key(backend=default_backend())
556-
key.verify(raw_signature,
557-
data=signed_info_c14n,
558-
signature_algorithm=ec.ECDSA(self._get_signature_digest_method(signature_alg)))
562+
dss_signature = self._encode_dss_signature(raw_signature, key.key_size)
563+
key.verify(
564+
dss_signature,
565+
data=signed_info_c14n,
566+
signature_algorithm=ec.ECDSA(
567+
self._get_signature_digest_method(signature_alg)
568+
),
569+
)
559570
elif "dsa-" in signature_alg:
560571
dsa_key_value = self._find(key_value, "DSAKeyValue")
561572
p = self._get_long(dsa_key_value, "P")
@@ -581,6 +592,18 @@ def _verify_signature_with_pubkey(self, signed_info_c14n, raw_signature, key_val
581592
else:
582593
raise NotImplementedError()
583594

595+
def _encode_dss_signature(self, raw_signature, key_size_bits):
596+
want_raw_signature_len = key_size_bits // 8 * 2
597+
if len(raw_signature) != want_raw_signature_len:
598+
raise InvalidSignature(
599+
"Expected %d byte SignatureValue, got %d"
600+
% (want_raw_signature_len, len(raw_signature))
601+
)
602+
int_len = len(raw_signature) // 2
603+
r = bytes_to_long(raw_signature[:int_len])
604+
s = bytes_to_long(raw_signature[int_len:])
605+
return utils.encode_dss_signature(r, s)
606+
584607
def _get_inclusive_ns_prefixes(self, transform_node):
585608
inclusive_namespaces = transform_node.find("./ec:InclusiveNamespaces[@PrefixList]", namespaces=namespaces)
586609
if inclusive_namespaces is None:
@@ -758,6 +781,10 @@ def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None
758781
raise InvalidSignature("Certificate subject common name mismatch")
759782

760783
signature_digest_method = self._get_signature_digest_method(signature_alg).name
784+
if "ecdsa-" in signature_alg:
785+
raw_signature = self._encode_dss_signature(
786+
raw_signature, signing_cert.get_pubkey().bits()
787+
)
761788
try:
762789
verify(signing_cert, raw_signature, signed_info_c14n, signature_digest_method)
763790
except OpenSSLCryptoError as e:

0 commit comments

Comments
 (0)