diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2015-10-06 22:50:37 +0200 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2015-10-06 23:39:18 +0200 |
commit | 671b6e2a751322ceb19b324041c78d856ec53d26 (patch) | |
tree | 12915831cc37f267b4f6c8ca0fc17189725f769c /pskc | |
parent | 68b20e272d5546c94bffd90002732a55696f8978 (diff) |
Support Python 3
This enables support for Python 3 together with Python 2 support with a
single codebase.
On Python 3 key data is passed around as bytestrings which makes the
doctests a little harder to maintain across Python versions.
Diffstat (limited to 'pskc')
-rw-r--r-- | pskc/__init__.py | 4 | ||||
-rw-r--r-- | pskc/crypto/aeskw.py | 22 | ||||
-rw-r--r-- | pskc/crypto/tripledeskw.py | 10 | ||||
-rw-r--r-- | pskc/encryption.py | 2 | ||||
-rw-r--r-- | pskc/key.py | 7 | ||||
-rw-r--r-- | pskc/policy.py | 4 | ||||
-rw-r--r-- | pskc/xml.py | 6 |
7 files changed, 31 insertions, 24 deletions
diff --git a/pskc/__init__.py b/pskc/__init__.py index 6edef85..8a7fa0c 100644 --- a/pskc/__init__.py +++ b/pskc/__init__.py @@ -1,7 +1,7 @@ # __init__.py - main module # coding: utf-8 # -# Copyright (C) 2014 Arthur de Jong +# Copyright (C) 2014-2015 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -34,7 +34,7 @@ The following prints all keys, decrypting using a password: >>> pskc = PSKC('tests/rfc6030-figure7.pskcxml') >>> pskc.encryption.derive_key('qwerty') >>> for key in pskc.keys: -... print key.serial, key.secret +... print('%s %s' % (key.serial, str(key.secret.decode()))) 987654321 12345678901234567890 The module should be able to handle most common PSKC files. Checking embedded diff --git a/pskc/crypto/aeskw.py b/pskc/crypto/aeskw.py index 24e90b0..eeafed1 100644 --- a/pskc/crypto/aeskw.py +++ b/pskc/crypto/aeskw.py @@ -1,7 +1,7 @@ # aeskw.py - implementation of AES key wrapping # coding: utf-8 # -# Copyright (C) 2014 Arthur de Jong +# Copyright (C) 2014-2015 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,6 +20,8 @@ """Implement key wrapping as described in RFC 3394 and RFC 5649.""" +import binascii + from Crypto.Cipher import AES from Crypto.Util.number import bytes_to_long, long_to_bytes from Crypto.Util.strxor import strxor @@ -31,8 +33,8 @@ def _split(value): return value[:8], value[8:] -RFC3394_IV = 'a6a6a6a6a6a6a6a6'.decode('hex') -RFC5649_IV = 'a65959a6'.decode('hex') +RFC3394_IV = binascii.a2b_hex('a6a6a6a6a6a6a6a6') +RFC5649_IV = binascii.a2b_hex('a65959a6') def wrap(plaintext, key, iv=None, pad=None): @@ -54,7 +56,7 @@ def wrap(plaintext, key, iv=None, pad=None): raise EncryptionError('Plaintext length wrong') if mli % 8 != 0 and pad is not False: r = (mli + 7) // 8 - plaintext += ((r * 8) - mli) * '\0' + plaintext += ((r * 8) - mli) * b'\0' if iv is None: if len(plaintext) != mli or pad is True: @@ -63,7 +65,7 @@ def wrap(plaintext, key, iv=None, pad=None): iv = RFC3394_IV encrypt = AES.new(key).encrypt - n = len(plaintext) / 8 + n = len(plaintext) // 8 if n == 1: # RFC 5649 shortcut @@ -76,7 +78,7 @@ def wrap(plaintext, key, iv=None, pad=None): for i in range(n): A, R[i] = _split(encrypt(A + R[i])) A = strxor(A, long_to_bytes(n * j + i + 1, 8)) - return A + ''.join(R) + return A + b''.join(R) def unwrap(ciphertext, key, iv=None, pad=None): @@ -95,7 +97,7 @@ def unwrap(ciphertext, key, iv=None, pad=None): raise DecryptionError('Ciphertext length wrong') decrypt = AES.new(key).decrypt - n = len(ciphertext) / 8 - 1 + n = len(ciphertext) // 8 - 1 if n == 1: A, plaintext = _split(decrypt(ciphertext)) @@ -107,16 +109,16 @@ def unwrap(ciphertext, key, iv=None, pad=None): for i in reversed(range(n)): A = strxor(A, long_to_bytes(n * j + i + 1, 8)) A, R[i] = _split(decrypt(A + R[i])) - plaintext = ''.join(R) + plaintext = b''.join(R) if iv is None: if A == RFC3394_IV and pad is not True: return plaintext elif A[:4] == RFC5649_IV and pad is not False: mli = bytes_to_long(A[4:]) - # check padding length is valid and only contains zeros + # check padding length is valid and plaintext only contains zeros if 8 * (n - 1) < mli <= 8 * n and \ - all(x == '\0' for x in plaintext[mli:]): + plaintext.endswith((len(plaintext) - mli) * b'\0'): return plaintext[:mli] elif A == iv: return plaintext diff --git a/pskc/crypto/tripledeskw.py b/pskc/crypto/tripledeskw.py index 47c93f1..a135ebd 100644 --- a/pskc/crypto/tripledeskw.py +++ b/pskc/crypto/tripledeskw.py @@ -1,7 +1,7 @@ # tripledeskw.py - implementation of Triple DES key wrapping # coding: utf-8 # -# Copyright (C) 2014 Arthur de Jong +# Copyright (C) 2014-2015 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -20,6 +20,8 @@ """Implement Triple DES key wrapping as described in RFC 3217.""" +import binascii + from Crypto import Random from Crypto.Cipher import DES3 from Crypto.Hash import SHA @@ -32,7 +34,7 @@ def _cms_hash(value): return SHA.new(value).digest()[:8] -RFC3217_IV = '4adda22c79e82105'.decode('hex') +RFC3217_IV = binascii.a2b_hex('4adda22c79e82105') def wrap(plaintext, key, iv=None): @@ -48,7 +50,7 @@ def wrap(plaintext, key, iv=None): cipher = DES3.new(key, DES3.MODE_CBC, iv) tmp = iv + cipher.encrypt(plaintext + _cms_hash(plaintext)) cipher = DES3.new(key, DES3.MODE_CBC, RFC3217_IV) - return cipher.encrypt(''.join(reversed(tmp))) + return cipher.encrypt(tmp[::-1]) def unwrap(ciphertext, key): @@ -59,7 +61,7 @@ def unwrap(ciphertext, key): if len(ciphertext) % DES3.block_size != 0: raise DecryptionError('Ciphertext length wrong') cipher = DES3.new(key, DES3.MODE_CBC, RFC3217_IV) - tmp = ''.join(reversed(cipher.decrypt(ciphertext))) + tmp = cipher.decrypt(ciphertext)[::-1] cipher = DES3.new(key, DES3.MODE_CBC, tmp[:8]) tmp = cipher.decrypt(tmp[8:]) if tmp[-8:] == _cms_hash(tmp[:-8]): diff --git a/pskc/encryption.py b/pskc/encryption.py index a9324e6..4911662 100644 --- a/pskc/encryption.py +++ b/pskc/encryption.py @@ -30,7 +30,7 @@ The encryption key can be derived using the KeyDerivation class. def unpad(value): """Remove padding from the plaintext.""" - return value[0:-ord(value[-1])] + return value[0:-ord(value[-1:])] class EncryptedValue(object): diff --git a/pskc/key.py b/pskc/key.py index 9e29e2e..af5f87d 100644 --- a/pskc/key.py +++ b/pskc/key.py @@ -1,7 +1,7 @@ # key.py - module for handling keys from pskc files # coding: utf-8 # -# Copyright (C) 2014 Arthur de Jong +# Copyright (C) 2014-2015 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -102,7 +102,10 @@ class BinaryDataType(DataType): def to_text(self, value): """Convert the value to an unencrypted string representation.""" - return base64.b64encode(value) + # force conversion to bytestring on Python 3 + if not isinstance(value, type(b'')): + value = value.encode() + return base64.b64encode(value).decode() def from_bin(self, value): """Convert the unencrypted binary to native representation.""" diff --git a/pskc/policy.py b/pskc/policy.py index 4f0c64c..94b7c06 100644 --- a/pskc/policy.py +++ b/pskc/policy.py @@ -1,7 +1,7 @@ # policy.py - module for handling PSKC policy information # coding: utf-8 # -# Copyright (C) 2014 Arthur de Jong +# Copyright (C) 2014-2015 Arthur de Jong # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -181,4 +181,4 @@ class Policy(object): """PIN value referenced by PINKeyId if any.""" key = self.pin_key if key: - return key.secret + return str(key.secret.decode()) diff --git a/pskc/xml.py b/pskc/xml.py index a45793a..1de2485 100644 --- a/pskc/xml.py +++ b/pskc/xml.py @@ -66,7 +66,7 @@ def find(tree, *matches): """Find a child element that matches any of the patterns (or None).""" for match in matches: try: - return iter(findall(tree, match)).next() + return next(iter(findall(tree, match))) except StopIteration: pass @@ -138,7 +138,7 @@ def mk_elem(parent, tag=None, text=None, empty=False, **kwargs): empty = True # don't create empty elements if not empty and text is None and \ - all(x is None for x in kwargs.itervalues()): + all(x is None for x in kwargs.values()): return # replace namespace identifier with URL if ':' in tag: @@ -152,7 +152,7 @@ def mk_elem(parent, tag=None, text=None, empty=False, **kwargs): if text is not None: element.text = _format(text) # set kwargs as attributes - for k, v in kwargs.iteritems(): + for k, v in kwargs.items(): if v is not None: element.set(k, _format(v)) return element |