Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
path: root/pskc
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2015-10-06 22:50:37 +0200
committerArthur de Jong <arthur@arthurdejong.org>2015-10-06 23:39:18 +0200
commit671b6e2a751322ceb19b324041c78d856ec53d26 (patch)
tree12915831cc37f267b4f6c8ca0fc17189725f769c /pskc
parent68b20e272d5546c94bffd90002732a55696f8978 (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__.py4
-rw-r--r--pskc/crypto/aeskw.py22
-rw-r--r--pskc/crypto/tripledeskw.py10
-rw-r--r--pskc/encryption.py2
-rw-r--r--pskc/key.py7
-rw-r--r--pskc/policy.py4
-rw-r--r--pskc/xml.py6
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