Skip to content

Commit 342e376

Browse files
committed
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 <[email protected]>
1 parent 448d627 commit 342e376

File tree

1 file changed

+43
-46
lines changed

1 file changed

+43
-46
lines changed

src/saml2/sigver.py

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import base64
77
import hashlib
8+
import itertools
89
import logging
910
import os
1011
import ssl
@@ -857,7 +858,8 @@ def validate_signature(self, signedtext, cert_file, cert_type, node_name, node_i
857858
:param cert_type: The file type of the certificate
858859
:param node_name: The name of the class that is signed
859860
:param node_id: The identifier of the node
860-
:param id_attr: Should normally be one of 'id', 'Id' or 'ID'
861+
:param id_attr: The attribute name for the identifier, normally one of
862+
'id','Id' or 'ID'
861863
:return: Boolean True if the signature was correct otherwise False.
862864
"""
863865
if not isinstance(signedtext, six.binary_type):
@@ -1350,67 +1352,60 @@ def decrypt_keys(self, enctext, keys=None, id_attr=''):
13501352
""" Decrypting an encrypted text by the use of a private key.
13511353
13521354
:param enctext: The encrypted text as a string
1355+
:param keys: Keys to try to decrypt enctext with
1356+
:param id_attr: The attribute name for the identifier, normally one of
1357+
'id','Id' or 'ID'
13531358
:return: The decrypted text
13541359
"""
1355-
_enctext = None
1356-
1357-
if not id_attr:
1358-
id_attr = self.id_attr
1360+
key_files = []
13591361

13601362
if not isinstance(keys, list):
13611363
keys = [keys]
13621364

1363-
if self.enc_key_files is not None:
1364-
for _enc_key_file in self.enc_key_files:
1365-
try:
1366-
_enctext = self.crypto.decrypt(enctext, _enc_key_file, id_attr)
1367-
except XmlsecError as e:
1368-
continue
1369-
else:
1370-
if _enctext:
1371-
return _enctext
1372-
1373-
for _key in keys:
1374-
if _key is not None and len(_key.strip()) > 0:
1375-
if not isinstance(_key, six.binary_type):
1376-
_key = str(_key).encode('ascii')
1377-
_, key_file = make_temp(_key, decode=False)
1378-
try:
1379-
_enctext = self.crypto.decrypt(enctext, key_file, id_attr)
1380-
except XmlsecError as e:
1381-
continue
1382-
else:
1383-
if _enctext:
1384-
return _enctext
1365+
keys = [key for key in keys if key]
1366+
for key in keys:
1367+
if not isinstance(key, six.binary_type):
1368+
key = key.encode("ascii")
1369+
_, key_file = make_temp(key, decode=False, delete=False)
1370+
key_files.append(key_file)
13851371

1386-
return enctext
1372+
try:
1373+
dectext = self.decrypt(enctext, key_file=key_files, id_attr=id_attr)
1374+
except DecryptError as e:
1375+
raise
1376+
else:
1377+
return dectext
1378+
finally:
1379+
for key_file in key_files:
1380+
os.unlink(key_file)
13871381

13881382
def decrypt(self, enctext, key_file=None, id_attr=''):
13891383
""" Decrypting an encrypted text by the use of a private key.
13901384
13911385
:param enctext: The encrypted text as a string
13921386
:return: The decrypted text
13931387
"""
1394-
_enctext = None
1395-
13961388
if not id_attr:
13971389
id_attr = self.id_attr
13981390

1399-
if self.enc_key_files is not None:
1400-
for _enc_key_file in self.enc_key_files:
1401-
try:
1402-
_enctext = self.crypto.decrypt(enctext, _enc_key_file, id_attr)
1403-
except XmlsecError:
1404-
continue
1405-
else:
1406-
if _enctext is not None and len(_enctext) > 0:
1407-
return _enctext
1391+
if not isinstance(key_file, list):
1392+
key_file = [key_file]
1393+
1394+
key_files = [
1395+
key for key in itertools.chain(key_file, self.enc_key_files) if key
1396+
]
1397+
for key_file in key_files:
1398+
try:
1399+
dectext = self.crypto.decrypt(enctext, key_file, id_attr)
1400+
except XmlsecError as e:
1401+
continue
1402+
else:
1403+
if dectext:
1404+
return dectext
14081405

1409-
if key_file is not None and len(key_file.strip()) > 0:
1410-
_enctext = self.crypto.decrypt(enctext, key_file, id_attr)
1411-
if _enctext is not None and len(_enctext) > 0:
1412-
return _enctext
1413-
return enctext
1406+
errmsg = "No key was able to decrypt the ciphertext. Keys tried: {keys}"
1407+
errmsg = errmsg.format(keys=key_files)
1408+
raise DecryptError(errmsg)
14141409

14151410
def verify_signature(self, signedtext, cert_file=None, cert_type='pem', node_name=NODE_NAME, node_id=None, id_attr=''):
14161411
""" Verifies the signature of a XML document.
@@ -1420,7 +1415,8 @@ def verify_signature(self, signedtext, cert_file=None, cert_type='pem', node_nam
14201415
:param cert_type: The file type of the certificate
14211416
:param node_name: The name of the class that is signed
14221417
:param node_id: The identifier of the node
1423-
:param id_attr: Should normally be one of 'id', 'Id' or 'ID'
1418+
:param id_attr: The attribute name for the identifier, normally one of
1419+
'id','Id' or 'ID'
14241420
:return: Boolean True if the signature was correct otherwise False.
14251421
"""
14261422
# This is only for testing purposes, otherwise when would you receive
@@ -1527,7 +1523,8 @@ def check_signature(self, item, node_name=NODE_NAME, origdoc=None, id_attr='', m
15271523
:param item: Parsed entity
15281524
:param node_name: The name of the node/class/element that is signed
15291525
:param origdoc: The original XML string
1530-
:param id_attr:
1526+
:param id_attr: The attribute name for the identifier, normally one of
1527+
'id','Id' or 'ID'
15311528
:param must:
15321529
:return:
15331530
"""

0 commit comments

Comments
 (0)