Skip to content

Commit 25092f6

Browse files
committed
Fixes for python 2 and 3 compatibility
1 parent 433005b commit 25092f6

10 files changed

Lines changed: 160 additions & 106 deletions

File tree

setup.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
#!/usr/bin/env python
22
from setuptools import setup, find_packages
3-
import sys, os
3+
import sys
4+
import os
45
from distutils import versionpredicate
56

67
here = os.path.abspath(os.path.dirname(__file__))
78
README = open(os.path.join(here, 'README.rst')).read()
89
NEWS = open(os.path.join(here, 'NEWS.txt')).read()
910

1011

11-
version = '0.19dev0'
12+
version = '0.19dev1'
1213

1314
install_requires = [
14-
'defusedxml', 'lxml', 'pyconfig', 'requests', 'cryptography'
15+
'defusedxml', 'lxml', 'pyconfig', 'requests', 'cryptography', 'six'
1516
]
1617

1718
# Let some other project depend on 'xmlsec[PKCS11]'

src/xmlsec/__init__.py

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55

66
__author__ = 'leifj'
77

8+
import six
89
from defusedxml import lxml
910
from lxml import etree as etree
1011
import logging
1112
import copy
1213
from lxml.builder import ElementMaker
1314
from xmlsec.exceptions import XMLSigException
1415
from xmlsec import constants
15-
from xmlsec.utils import parse_xml, pem2b64, unescape_xml_entities, delete_elt, root_elt, b64d, b64e
16+
from xmlsec.utils import parse_xml, pem2b64, unescape_xml_entities, delete_elt, root_elt, b64d, b64e, etree_to_string
1617
import xmlsec.crypto
1718
import pyconfig
1819

@@ -38,7 +39,7 @@ class Config(object):
3839
default_signature_alg = pyconfig.setting("xmlsec.default_signature_alg", constants.ALGORITHM_SIGNATURE_RSA_SHA256)
3940
default_digest_alg = pyconfig.setting("xmlsec.default_digest_alg", constants.ALGORITHM_DIGEST_SHA256)
4041
default_c14n_alg = pyconfig.setting("xmlsec.default_c14n_alg", constants.TRANSFORM_C14N_EXCLUSIVE)
41-
debug_write_to_files = pyconfig.setting("xmlsec.config.debug_write_to_files", False)
42+
debug_write_to_files = pyconfig.setting("xmlsec.config.debug_write_to_files", True)
4243
same_document_is_root = pyconfig.setting("xmlsec.same_document_is_root", False)
4344
id_attributes = pyconfig.setting("xmlsec.id_attributes", ['ID', 'id'])
4445
c14n_strip_ws = pyconfig.setting("xmlsec.c14n_strip_ws", False)
@@ -81,9 +82,9 @@ def _signed_value(data, key_size, do_pad, hash_alg): # TODO Do proper asn1 CMS
8182
if do_pad:
8283
# Pad to "one octet shorter than the RSA modulus" [RSA-SHA1]
8384
# WARNING: key size is in bits, not bytes!
84-
padded_size = key_size / 8 - 1
85+
padded_size = key_size // 8 - 1
8586
pad_size = padded_size - len(asn_digest) - 2
86-
pad = '\x01' + '\xFF' * pad_size + '\x00'
87+
pad = b'\x01' + b'\xFF' * pad_size + b'\x00'
8788
return pad + asn_digest
8889
else:
8990
return asn_digest
@@ -151,7 +152,7 @@ def _process_references(t, sig, verify_mode=True, sig_path=".//{%s}Signature" %
151152

152153
if config.debug_write_to_files:
153154
with open("/tmp/foo-pre-transform.xml", "w") as fd:
154-
fd.write(etree.tostring(obj))
155+
fd.write(etree_to_string(obj))
155156

156157
for tr in ref.findall(".//{%s}Transform" % NS['ds']):
157158
obj = _transform(_alg(tr), obj, tr=tr, sig_path=sig_path)
@@ -162,14 +163,16 @@ def _process_references(t, sig, verify_mode=True, sig_path=".//{%s}Signature" %
162163
if nsprefix in r.nsmap:
163164
obj_copy.nsmap[nsprefix] = r.nsmap[nsprefix]
164165

165-
if not isinstance(obj, basestring):
166+
if not isinstance(obj, six.string_types):
166167
if config.debug_write_to_files:
167168
with open("/tmp/foo-pre-serialize.xml", "w") as fd:
168-
fd.write(etree.tostring(obj))
169+
fd.write(etree_to_string(obj))
169170
obj = _transform(constants.TRANSFORM_C14N_INCLUSIVE, obj)
170171

171172
if config.debug_write_to_files:
172173
with open("/tmp/foo-obj.xml", "w") as fd:
174+
if six.PY2:
175+
obj = obj.encode('utf-8')
173176
fd.write(obj)
174177

175178
hash_alg = _ref_digest(ref)
@@ -215,7 +218,7 @@ def _enveloped_signature(t, sig_path=".//{%s}Signature" % NS['ds']):
215218
delete_elt(sig)
216219
if config.debug_write_to_files:
217220
with open("/tmp/foo-env.xml", "w") as fd:
218-
fd.write(etree.tostring(t))
221+
fd.write(etree_to_string(t))
219222
return t
220223

221224

@@ -231,15 +234,17 @@ def _c14n(t, exclusive, with_comments, inclusive_prefix_list=None, schema=None):
231234
"""
232235
doc = t
233236
if root_elt(doc).getparent() is not None:
234-
xml_str = etree.tostring(doc, encoding=unicode)
237+
xml_str = etree_to_string(doc)
235238
doc = parse_xml(xml_str, remove_whitespace=config.c14n_strip_ws, remove_comments=not with_comments, schema=schema)
236239
del xml_str
237240

238-
buf = etree.tostring(doc,
239-
method='c14n',
240-
exclusive=exclusive,
241-
with_comments=with_comments,
242-
inclusive_ns_prefixes=inclusive_prefix_list)
241+
buf = six.text_type(
242+
etree.tostring(doc,
243+
method='c14n',
244+
exclusive=exclusive,
245+
with_comments=with_comments,
246+
inclusive_ns_prefixes=inclusive_prefix_list),
247+
'utf-8')
243248
#u = unescape_xml_entities(buf.decode("utf8", 'strict')).encode("utf8").strip()
244249
assert buf[0] == '<'
245250
assert buf[-1] == '>'
@@ -292,7 +297,7 @@ def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=F
292297
"""
293298
if config.debug_write_to_files:
294299
with open("/tmp/foo-sig.xml", "w") as fd:
295-
fd.write(etree.tostring(root_elt(t)))
300+
fd.write(etree_to_string(t))
296301

297302
validated = []
298303
for sig in t.findall(sig_path):
@@ -330,7 +335,7 @@ def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=F
330335
if not this_cert.verify(b64d(sv), actual, sig_digest_alg):
331336
raise XMLSigException("Failed to validate {!s} using sig digest {!s} and cm {!s}".format(etree.tostring(sig), sig_digest_alg, cm_alg))
332337
validated.append(obj)
333-
except XMLSigException, ex:
338+
except XMLSigException as ex:
334339
logging.error(ex)
335340

336341
if not validated:
@@ -433,8 +438,8 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path
433438
public.keysize, private.keysize))
434439
# This might be incorrect for PKCS#11 tokens if we have no public key
435440
logging.debug("Using {!s} bit key".format(private.keysize))
436-
437-
templates = filter(_is_template, t.findall(sig_path))
441+
sig_paths = t.findall(sig_path)
442+
templates = list(filter(_is_template, sig_paths))
438443
if not templates:
439444
tmpl = add_enveloped_signature(t, reference_uri=reference_uri, pos=insert_index)
440445
templates = [tmpl]
@@ -443,7 +448,7 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path
443448

444449
if config.debug_write_to_files:
445450
with open("/tmp/sig-ref.xml", "w") as fd:
446-
fd.write(etree.tostring(root_elt(t)))
451+
fd.write(etree_to_string(root_elt(t)))
447452

448453
for sig in templates:
449454
logging.debug("processing sig template: %s" % etree.tostring(sig))
@@ -469,6 +474,8 @@ def sign(t, key_spec, cert_spec=None, reference_uri='', insert_index=0, sig_path
469474

470475
signed = private.sign(tbs, sig_alg)
471476
signature = b64e(signed)
477+
if isinstance(signature, six.binary_type):
478+
signature = six.text_type(signature, 'utf-8')
472479
logging.debug("SignatureValue: %s" % signature)
473480
sv = sig.find(".//{%s}SignatureValue" % NS['ds'])
474481
if sv is None:

src/xmlsec/constants.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@
2121
ALGORITHM_SIGNATURE_RSA_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
2222
ALGORITHM_SIGNATURE_RSA_SHA512 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
2323

24-
2524
# ASN.1 BER SHA1 algorithm designator prefixes (RFC3447)
2625
ASN1_BER_ALG_DESIGNATOR_PREFIX = {
2726
# disabled 'md2': '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x02\x05\x00\x04\x10',
2827
# disabled 'md5': '\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10',
29-
_SHA1_INTERNAL: '\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14',
30-
_SHA256_INTERNAL: '\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20',
31-
_SHA384_INTERNAL: '\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30',
32-
_SHA512_INTERNAL: '\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40',
28+
_SHA1_INTERNAL: b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14',
29+
_SHA256_INTERNAL: b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20',
30+
_SHA384_INTERNAL: b'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30',
31+
_SHA512_INTERNAL: b'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40',
3332
}
3433

3534
sign_alg_xmldsig_digest_to_internal_d = {

src/xmlsec/crypto.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,20 @@
33
import base64
44
import logging
55
import threading
6+
import six
67
from binascii import hexlify
7-
from UserDict import DictMixin
88
from xmlsec.exceptions import XMLSigException
9+
from xmlsec.utils import unicode_to_bytes
910
from cryptography.exceptions import InvalidSignature
1011
from cryptography.hazmat.backends import default_backend
1112
from cryptography.hazmat.primitives import serialization, hashes
1213
from cryptography.hazmat.primitives.asymmetric import rsa, padding, utils
1314
from cryptography.x509 import load_pem_x509_certificate, load_der_x509_certificate, Certificate
1415

16+
if six.PY2:
17+
from UserDict import DictMixin
18+
else:
19+
from collections import MutableMapping as DictMixin
1520

1621
NS = {'ds': 'http://www.w3.org/2000/09/xmldsig#'}
1722

@@ -85,6 +90,8 @@ def __init__(self, source, do_padding, private, do_digest=True):
8590

8691
def sign(self, data, hash_alg, pad_alg="PKCS1v15"):
8792
if self.is_private:
93+
if not isinstance(data, six.binary_type):
94+
data = unicode_to_bytes(data)
8895
hasher = getattr(hashes, hash_alg)
8996
padder = getattr(padding, pad_alg)
9097
return self.key.sign(data, padder(), hasher())
@@ -93,6 +100,8 @@ def sign(self, data, hash_alg, pad_alg="PKCS1v15"):
93100

94101
def verify(self, signature, msg, hash_alg, pad_alg="PKCS1v15"):
95102
if not self.is_private:
103+
if not isinstance(msg, six.binary_type):
104+
msg = unicode_to_bytes(msg)
96105
try:
97106
hasher = getattr(hashes, hash_alg)
98107
padder = getattr(padding, pad_alg)
@@ -221,7 +230,7 @@ def sign(self, data, hash_alg=None):
221230
if not 'signed' in msg:
222231
raise ValueError("Missing signed data in response message")
223232
return msg['signed'].decode('base64')
224-
except Exception, ex:
233+
except Exception as ex:
225234
from traceback import print_exc
226235
print_exc(ex)
227236
raise XMLSigException(ex)
@@ -230,7 +239,7 @@ def sign(self, data, hash_alg=None):
230239
def _load_keyspec(keyspec, private=False, signature_element=None):
231240
if private and hasattr(keyspec, '__call__'):
232241
return XMLSecCryptoCallable(keyspec)
233-
if isinstance(keyspec, basestring):
242+
if isinstance(keyspec, six.string_types):
234243
if os.path.isfile(keyspec):
235244
return XMLSecCryptoFile(keyspec, private)
236245
elif private and keyspec.startswith("pkcs11://"):
@@ -274,6 +283,13 @@ def __setitem__(self, key, value):
274283
def __delitem__(self, key):
275284
del self.certs[key]
276285

286+
def __len__(self):
287+
return len(self.certs)
288+
289+
def __iter__(self):
290+
for item in self.certs:
291+
yield item
292+
277293
def _get_cert_by_fp(self, fp):
278294
"""
279295
Get the cryptography.x509.Certificate representation.
@@ -320,6 +336,7 @@ def _find_cert_by_fingerprint(t, fp):
320336

321337
return cert.public_bytes(encoding=serialization.Encoding.PEM)
322338

339+
323340
def _digest(data, hash_alg):
324341
"""
325342
Calculate a hash digest of algorithm hash_alg and return the result base64 encoded.
@@ -330,5 +347,7 @@ def _digest(data, hash_alg):
330347
"""
331348
h = getattr(hashes, hash_alg)
332349
d = hashes.Hash(h(), backend=default_backend())
350+
if not isinstance(data, six.binary_type):
351+
data = unicode_to_bytes(data)
333352
d.update(data)
334353
return base64.b64encode(d.finalize())

src/xmlsec/pk11.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
__author__ = 'leifj'
55

66
from xmlsec.exceptions import XMLSigException
7-
from urlparse import urlparse
7+
from six.moves.urllib_parse import urlparse
88
import os
99
import logging
1010
from xmlsec.utils import b642pem
@@ -17,7 +17,7 @@
1717
except ImportError:
1818
raise XMLSigException("pykcs11 is required for PKCS#11 keys - cf README.rst")
1919

20-
all_attributes = PyKCS11.CKA.keys()
20+
all_attributes = list(PyKCS11.CKA.keys())
2121

2222
# remove the CKR_ATTRIBUTE_SENSITIVE attributes since we can't get
2323
all_attributes.remove(PyKCS11.LowLevel.CKA_PRIVATE_EXPONENT)
@@ -81,7 +81,7 @@ def parse_uri(pk11_uri):
8181

8282

8383
def _intarray2bytes(x):
84-
return ''.join(chr(i) for i in x)
84+
return bytearray(x)
8585

8686

8787
def _close_session(session):

src/xmlsec/test/case.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import os
88
import pkg_resources
99
from defusedxml import lxml
10-
import lxml.etree as etree
11-
from StringIO import StringIO
10+
from lxml import etree
11+
from six.moves import StringIO
1212
import xmlsec
1313

1414

src/xmlsec/test/p11_test.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from xmlsec.test import run_cmd
2222

2323
from xmlsec.test.case import load_test_data
24+
from xmlsec.exceptions import XMLSigException
2425

2526
try:
2627
from PyKCS11 import PyKCS11Error
@@ -29,8 +30,8 @@
2930
raise unittest.SkipTest("PyKCS11 not installed")
3031

3132
try:
32-
import xmlsec.pk11 as pk11
33-
except Exception:
33+
from xmlsec import pk11
34+
except (ImportError, XMLSigException):
3435
raise unittest.SkipTest("PyKCS11 not installed")
3536

3637

@@ -211,9 +212,9 @@ def setup():
211212
'--pin', 'secret1'], softhsm_conf=softhsm_conf)
212213

213214
except Exception as ex:
214-
print "-" * 64
215+
print("-" * 64)
215216
traceback.print_exc()
216-
print "-" * 64
217+
print("-" * 64)
217218
logging.warning("PKCS11 tests disabled: unable to initialize test token: %s" % ex)
218219

219220

@@ -253,7 +254,7 @@ def test_open_session(self):
253254
os.environ['SOFTHSM2_CONF'] = softhsm_conf
254255
session = pk11._session(component_path['P11_MODULE'], pk11_uri="pkcs11://%s/test?pin=secret1" % component_path['P11_MODULE'])
255256
assert session is not None
256-
except Exception, ex:
257+
except Exception as ex:
257258
traceback.print_exc()
258259
raise ex
259260
finally:
@@ -268,7 +269,7 @@ def test_open_session_no_pin(self):
268269
os.environ['SOFTHSM2_CONF'] = softhsm_conf
269270
session = pk11._session(component_path['P11_MODULE'], pk11_uri="pkcs11://%s/test" % component_path['P11_MODULE'])
270271
assert session is not None
271-
except Exception, ex:
272+
except Exception as ex:
272273
traceback.print_exc()
273274
raise ex
274275
finally:
@@ -288,7 +289,7 @@ def test_two_sessions(self):
288289
assert session1 != session2
289290
assert session1 is not None
290291
assert session2 is not None
291-
except Exception, ex:
292+
except Exception as ex:
292293
raise ex
293294
finally:
294295
if session1 is not None:
@@ -303,7 +304,7 @@ def test_bad_login(self):
303304
try:
304305
session = pk11._session(component_path['P11_MODULE'], pk11_uri="pkcs11://%s/test?pin=wrong" % component_path['P11_MODULE'])
305306
assert False, "We should have failed the last login"
306-
except PyKCS11Error, ex:
307+
except PyKCS11Error as ex:
307308
assert ex.value == CKR_PIN_INCORRECT
308309
pass
309310

@@ -317,8 +318,8 @@ def test_find_key(self):
317318
key, cert = pk11._find_key(session, "test")
318319
assert key is not None
319320
assert cert is not None
320-
assert cert.strip() == open(signer_cert_pem).read().strip()
321-
except Exception, ex:
321+
assert cert.strip() == open(signer_cert_pem).read().strip().encode('utf-8')
322+
except Exception as ex:
322323
raise ex
323324
finally:
324325
if session is not None:

0 commit comments

Comments
 (0)