Skip to content

Commit d16ba95

Browse files
authored
Fix #143 Allow the combination of X509Data and KeyValue when they represent the same public key (#169)
1 parent 29f3820 commit d16ba95

File tree

1 file changed

+98
-5
lines changed

1 file changed

+98
-5
lines changed

signxml/__init__.py

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,6 @@ def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None
820820
algorithm=c14n_algorithm,
821821
inclusive_ns_prefixes=inclusive_ns_prefixes)
822822

823-
# TODO: if both X509Data and KeyValue is present, match one against the other and raise an error on mismatch
824823
if x509_data is not None or self.require_x509:
825824
from OpenSSL.crypto import load_certificate, X509, FILETYPE_PEM, verify, Error as OpenSSLCryptoError
826825

@@ -869,10 +868,19 @@ def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None
869868
reason = e
870869
raise InvalidSignature("Signature verification failed: {}".format(reason))
871870

872-
if ignore_ambiguous_key_info is False:
873-
if key_value is not None or der_encoded_key_value is not None:
874-
raise InvalidInput("Both X509Data and KeyValue found. Use verify(ignore_ambiguous_key_info=True) "
875-
"to ignore KeyValue and validate using X509Data only.")
871+
# If both X509Data and KeyValue are present, match one against the other and raise an error on mismatch
872+
if key_value is not None:
873+
if self.check_key_value_matches_cert_public_key(key_value, signing_cert.get_pubkey(), signature_alg) is False:
874+
if ignore_ambiguous_key_info is False:
875+
raise InvalidInput("Both X509Data and KeyValue found and they represent different public keys. "
876+
"Use verify(ignore_ambiguous_key_info=True) to ignore KeyValue and validate using X509Data only.")
877+
878+
# If both X509Data and DEREncodedKeyValue are present, match one against the other and raise an error on mismatch
879+
if der_encoded_key_value is not None:
880+
if self.check_der_key_value_matches_cert_public_key(der_encoded_key_value, signing_cert.get_pubkey(), signature_alg) is False:
881+
if ignore_ambiguous_key_info is False:
882+
raise InvalidInput("Both X509Data and DEREncodedKeyValue found and they represent different public keys. "
883+
"Use verify(ignore_ambiguous_key_info=True) to ignore DEREncodedKeyValue and validate using X509Data only.")
876884

877885
# TODO: CN verification goes here
878886
# TODO: require one of the following to be set: either x509_cert or (ca_pem_file or ca_path) or common_name
@@ -923,6 +931,91 @@ def verify(self, data, require_x509=True, x509_cert=None, cert_subject_name=None
923931

924932
return verify_results if expect_references > 1 else verify_results[0]
925933

934+
def check_key_value_matches_cert_public_key(self, key_value, public_key, signature_alg):
935+
if "ecdsa-" in signature_alg \
936+
and isinstance(public_key.to_cryptography_key(), ec.EllipticCurvePublicKey):
937+
ec_key_value = self._find(key_value, "ECKeyValue", namespace="dsig11")
938+
named_curve = self._find(ec_key_value, "NamedCurve", namespace="dsig11")
939+
public_key = self._find(ec_key_value, "PublicKey", namespace="dsig11")
940+
key_data = b64decode(public_key.text)[1:]
941+
x = bytes_to_long(key_data[:len(key_data)//2])
942+
y = bytes_to_long(key_data[len(key_data)//2:])
943+
curve_class = self.known_ecdsa_curves[named_curve.get("URI")]
944+
945+
pubk_curve = public_key.to_cryptography_key().public_numbers().curve
946+
pubk_x = public_key.to_cryptography_key().public_numbers().x
947+
pubk_y = public_key.to_cryptography_key().public_numbers().y
948+
949+
return curve_class == pubk_curve and x == pubk_x and y == pubk_y
950+
951+
elif "dsa-" in signature_alg \
952+
and isinstance(public_key.to_cryptography_key(), dsa.DSAPublicKey):
953+
dsa_key_value = self._find(key_value, "DSAKeyValue")
954+
p = self._get_long(dsa_key_value, "P")
955+
q = self._get_long(dsa_key_value, "Q")
956+
g = self._get_long(dsa_key_value, "G", require=False)
957+
958+
pubk_p = public_key.to_cryptography_key().public_numbers().p
959+
pubk_q = public_key.to_cryptography_key().public_numbers().q
960+
pubk_g = public_key.to_cryptography_key().public_numbers().g
961+
962+
return p == pubk_p and q == pubk_q and g == pubk_g
963+
964+
elif "rsa-" in signature_alg \
965+
and isinstance(public_key.to_cryptography_key(), rsa.RSAPublicKey):
966+
rsa_key_value = self._find(key_value, "RSAKeyValue")
967+
n = self._get_long(rsa_key_value, "Modulus")
968+
e = self._get_long(rsa_key_value, "Exponent")
969+
970+
pubk_n = public_key.to_cryptography_key().public_numbers().n
971+
pubk_e = public_key.to_cryptography_key().public_numbers().e
972+
973+
return n == pubk_n and e == pubk_e
974+
975+
raise NotImplementedError()
976+
977+
def check_der_key_value_matches_cert_public_key(self, der_encoded_key_value, public_key, signature_alg):
978+
der_public_key = load_der_public_key(b64decode(der_encoded_key_value.text), backend=default_backend())
979+
980+
if "ecdsa-" in signature_alg \
981+
and isinstance(der_public_key, ec.EllipticCurvePublicKey) \
982+
and isinstance(public_key.to_cryptography_key(), ec.EllipticCurvePublicKey):
983+
curve_class = der_public_key.public_numbers().curve
984+
x = der_public_key.public_numbers().x
985+
y = der_public_key.public_numbers().y
986+
987+
pubk_curve = public_key.to_cryptography_key().public_numbers().curve
988+
pubk_x = public_key.to_cryptography_key().public_numbers().x
989+
pubk_y = public_key.to_cryptography_key().public_numbers().y
990+
991+
return curve_class == pubk_curve and x == pubk_x and y == pubk_y
992+
993+
elif "dsa-" in signature_alg \
994+
and isinstance(der_public_key, dsa.DSAPublicKey) \
995+
and isinstance(public_key.to_cryptography_key(), dsa.DSAPublicKey):
996+
p = der_public_key.public_numbers().parameter_numbers().p
997+
q = der_public_key.public_numbers().parameter_numbers().q
998+
g = der_public_key.public_numbers().parameter_numbers().g
999+
1000+
pubk_p = public_key.to_cryptography_key().public_numbers().p
1001+
pubk_q = public_key.to_cryptography_key().public_numbers().q
1002+
pubk_g = public_key.to_cryptography_key().public_numbers().g
1003+
1004+
return p == pubk_p and q == pubk_q and g == pubk_g
1005+
1006+
elif "rsa-" in signature_alg \
1007+
and isinstance(der_public_key, rsa.RSAPublicKey) \
1008+
and isinstance(public_key.to_cryptography_key(), rsa.RSAPublicKey):
1009+
n = der_public_key.public_numbers().n
1010+
e = der_public_key.public_numbers().e
1011+
1012+
pubk_n = public_key.to_cryptography_key().public_numbers().n
1013+
pubk_e = public_key.to_cryptography_key().public_numbers().e
1014+
1015+
return n == pubk_n and e == pubk_e
1016+
1017+
raise NotImplementedError()
1018+
9261019
def _get_long(self, element, query, require=True):
9271020
result = self._find(element, query, require=require)
9281021
if result is not None:

0 commit comments

Comments
 (0)