diff options
author | Ivan Kanakarakis <ivan.kanak@gmail.com> | 2019-01-10 22:37:58 +0200 |
---|---|---|
committer | Ivan Kanakarakis <ivan.kanak@gmail.com> | 2019-01-14 15:29:43 +0200 |
commit | 342e376a4aaab2a5eeafb41f1d971019be887404 (patch) | |
tree | 57adc345c6be20a64c74e723b740ec51273504e1 | |
parent | 448d6276f1c99bd2d48f9e4acc744e56c3da008c (diff) | |
download | pysaml2-342e376a4aaab2a5eeafb41f1d971019be887404.tar.gz |
Refactor SecurityContext::decrypt to report failures
Previously there was no reliable way to know whether decryption failed. Now,
when decryption fails an DecryptError exception is raised containing the keys
that were tried to decrypt the given ciphertext.
The same refactoring is done to SecurityContext::decrypt_keys.
Signed-off-by: Ivan Kanakarakis <ivan.kanak@gmail.com>
-rw-r--r-- | src/saml2/sigver.py | 89 |
1 files changed, 43 insertions, 46 deletions
diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index 1cb0bec4..0e3e0839 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -5,6 +5,7 @@ from OpenSSL import crypto import base64 import hashlib +import itertools import logging import os import ssl @@ -857,7 +858,8 @@ class CryptoBackendXmlSec1(CryptoBackend): :param cert_type: The file type of the certificate :param node_name: The name of the class that is signed :param node_id: The identifier of the node - :param id_attr: Should normally be one of 'id', 'Id' or 'ID' + :param id_attr: The attribute name for the identifier, normally one of + 'id','Id' or 'ID' :return: Boolean True if the signature was correct otherwise False. """ if not isinstance(signedtext, six.binary_type): @@ -1350,40 +1352,32 @@ class SecurityContext(object): """ Decrypting an encrypted text by the use of a private key. :param enctext: The encrypted text as a string + :param keys: Keys to try to decrypt enctext with + :param id_attr: The attribute name for the identifier, normally one of + 'id','Id' or 'ID' :return: The decrypted text """ - _enctext = None - - if not id_attr: - id_attr = self.id_attr + key_files = [] if not isinstance(keys, list): keys = [keys] - if self.enc_key_files is not None: - for _enc_key_file in self.enc_key_files: - try: - _enctext = self.crypto.decrypt(enctext, _enc_key_file, id_attr) - except XmlsecError as e: - continue - else: - if _enctext: - return _enctext - - for _key in keys: - if _key is not None and len(_key.strip()) > 0: - if not isinstance(_key, six.binary_type): - _key = str(_key).encode('ascii') - _, key_file = make_temp(_key, decode=False) - try: - _enctext = self.crypto.decrypt(enctext, key_file, id_attr) - except XmlsecError as e: - continue - else: - if _enctext: - return _enctext + keys = [key for key in keys if key] + for key in keys: + if not isinstance(key, six.binary_type): + key = key.encode("ascii") + _, key_file = make_temp(key, decode=False, delete=False) + key_files.append(key_file) - return enctext + try: + dectext = self.decrypt(enctext, key_file=key_files, id_attr=id_attr) + except DecryptError as e: + raise + else: + return dectext + finally: + for key_file in key_files: + os.unlink(key_file) def decrypt(self, enctext, key_file=None, id_attr=''): """ Decrypting an encrypted text by the use of a private key. @@ -1391,26 +1385,27 @@ class SecurityContext(object): :param enctext: The encrypted text as a string :return: The decrypted text """ - _enctext = None - if not id_attr: id_attr = self.id_attr - if self.enc_key_files is not None: - for _enc_key_file in self.enc_key_files: - try: - _enctext = self.crypto.decrypt(enctext, _enc_key_file, id_attr) - except XmlsecError: - continue - else: - if _enctext is not None and len(_enctext) > 0: - return _enctext + if not isinstance(key_file, list): + key_file = [key_file] + + key_files = [ + key for key in itertools.chain(key_file, self.enc_key_files) if key + ] + for key_file in key_files: + try: + dectext = self.crypto.decrypt(enctext, key_file, id_attr) + except XmlsecError as e: + continue + else: + if dectext: + return dectext - if key_file is not None and len(key_file.strip()) > 0: - _enctext = self.crypto.decrypt(enctext, key_file, id_attr) - if _enctext is not None and len(_enctext) > 0: - return _enctext - return enctext + errmsg = "No key was able to decrypt the ciphertext. Keys tried: {keys}" + errmsg = errmsg.format(keys=key_files) + raise DecryptError(errmsg) def verify_signature(self, signedtext, cert_file=None, cert_type='pem', node_name=NODE_NAME, node_id=None, id_attr=''): """ Verifies the signature of a XML document. @@ -1420,7 +1415,8 @@ class SecurityContext(object): :param cert_type: The file type of the certificate :param node_name: The name of the class that is signed :param node_id: The identifier of the node - :param id_attr: Should normally be one of 'id', 'Id' or 'ID' + :param id_attr: The attribute name for the identifier, normally one of + 'id','Id' or 'ID' :return: Boolean True if the signature was correct otherwise False. """ # This is only for testing purposes, otherwise when would you receive @@ -1527,7 +1523,8 @@ class SecurityContext(object): :param item: Parsed entity :param node_name: The name of the node/class/element that is signed :param origdoc: The original XML string - :param id_attr: + :param id_attr: The attribute name for the identifier, normally one of + 'id','Id' or 'ID' :param must: :return: """ |