Skip to content

Commit d724f8d

Browse files
authored
Merge pull request #62 from SUNET/fix_python3
Python 3 compatibilty
2 parents e0cd024 + 1ec6b05 commit d724f8d

11 files changed

Lines changed: 176 additions & 108 deletions

File tree

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
sudo: false
2+
dist: xenial
23
language: python
34
python:
45
- 2.7
6+
- 3.5
7+
- 3.6
8+
- 3.7
59

610
addons:
711
apt:

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: 25 additions & 18 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

@@ -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

0 commit comments

Comments
 (0)