lists.arthurdejong.org
RSS feed

python-pskc branch master updated. 0.5-19-g2c8a9b7

[Date Prev][Date Next] [Thread Prev][Thread Next]

python-pskc branch master updated. 0.5-19-g2c8a9b7



This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-pskc".

The branch, master has been updated
       via  2c8a9b7b1b87a3cc07aac800d77db4652165d3e1 (commit)
       via  d0eddf88287bce398cb7a55ee20b384e3f764a4d (commit)
      from  4ed4e115469e756b879e4cc788391eca0d656d1c (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://arthurdejong.org/git/python-pskc/commit/?id=2c8a9b7b1b87a3cc07aac800d77db4652165d3e1

commit 2c8a9b7b1b87a3cc07aac800d77db4652165d3e1
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat Sep 30 18:28:37 2017 +0200

    Replace use of pycrypto utility functions
    
    This uses os.urandom() as a source for random data and replaces other
    utility functions. This also removes one import for getting the lengths
    of Tripple DES keys.

diff --git a/pskc/crypto/aeskw.py b/pskc/crypto/aeskw.py
index 416d79b..7396f26 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-2015 Arthur de Jong
+# Copyright (C) 2014-2017 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
@@ -21,14 +21,21 @@
 """Implement key wrapping as described in RFC 3394 and RFC 5649."""
 
 import binascii
+import struct
 
 from Crypto.Cipher import AES
-from Crypto.Util.number import bytes_to_long, long_to_bytes
-from Crypto.Util.strxor import strxor
 
 from pskc.exceptions import DecryptionError, EncryptionError
 
 
+def _strxor(a, b):
+    """Return a XOR b."""
+    if isinstance(b'', str):  # pragma: no cover (Python 2 specific)
+        return b''.join(chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b))
+    else:  # pragma: no cover (Python 3 specific)
+        return bytes(x ^ y for (x, y) in zip(a, b))
+
+
 def _split(value):
     return value[:8], value[8:]
 
@@ -60,7 +67,7 @@ def wrap(plaintext, key, iv=None, pad=None):
 
     if iv is None:
         if len(plaintext) != mli or pad is True:
-            iv = RFC5649_IV + long_to_bytes(mli, 4)
+            iv = RFC5649_IV + struct.pack('>I', mli)
         else:
             iv = RFC3394_IV
 
@@ -77,7 +84,7 @@ def wrap(plaintext, key, iv=None, pad=None):
     for j in range(6):
         for i in range(n):
             A, R[i] = _split(encrypt(A + R[i]))
-            A = strxor(A, long_to_bytes(n * j + i + 1, 8))
+            A = _strxor(A, struct.pack('>Q', n * j + i + 1))
     return A + b''.join(R)
 
 
@@ -107,7 +114,7 @@ def unwrap(ciphertext, key, iv=None, pad=None):
              for i in range(n)]
         for j in reversed(range(6)):
             for i in reversed(range(n)):
-                A = strxor(A, long_to_bytes(n * j + i + 1, 8))
+                A = _strxor(A, struct.pack('>Q', n * j + i + 1))
                 A, R[i] = _split(decrypt(A + R[i]))
         plaintext = b''.join(R)
 
@@ -115,7 +122,7 @@ def unwrap(ciphertext, key, iv=None, pad=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:])
+            mli = struct.unpack('>I', A[4:])[0]
             # check padding length is valid and plaintext only contains zeros
             if 8 * (n - 1) < mli <= 8 * n and \
                plaintext.endswith((len(plaintext) - mli) * b'\0'):
diff --git a/pskc/crypto/tripledeskw.py b/pskc/crypto/tripledeskw.py
index 0802141..9494026 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-2015 Arthur de Jong
+# Copyright (C) 2014-2017 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
@@ -21,8 +21,8 @@
 """Implement Triple DES key wrapping as described in RFC 3217."""
 
 import binascii
+import os
 
-from Crypto import Random
 from Crypto.Cipher import DES3
 from Crypto.Hash import SHA
 
@@ -47,7 +47,7 @@ def wrap(plaintext, key, iv=None):
     if len(plaintext) % DES3.block_size != 0:
         raise EncryptionError('Plaintext length wrong')
     if iv is None:
-        iv = Random.get_random_bytes(8)
+        iv = os.urandom(8)
     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)
diff --git a/pskc/encryption.py b/pskc/encryption.py
index 0da9f82..5ae31db 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -27,6 +27,9 @@ The encryption key can be derived using the KeyDerivation 
class.
 """
 
 
+import os
+
+
 def algorithm_key_lengths(algorithm):
     """Return the possible key lengths for the configured algorithm."""
     from pskc.exceptions import DecryptionError
@@ -38,8 +41,7 @@ def algorithm_key_lengths(algorithm):
         return [int(algorithm[-7:-4]) // 8]
     elif algorithm.endswith('#tripledes-cbc') or \
             algorithm.endswith('#kw-tripledes'):
-        from Crypto.Cipher import DES3
-        return list(DES3.key_size)
+        return [16, 24]
     elif algorithm.endswith('#kw-aes128') or \
             algorithm.endswith('#kw-aes192') or \
             algorithm.endswith('#kw-aes256'):
@@ -98,17 +100,15 @@ def encrypt(algorithm, key, plaintext, iv=None):
     if algorithm.endswith('#aes128-cbc') or \
             algorithm.endswith('#aes192-cbc') or \
             algorithm.endswith('#aes256-cbc'):
-        from Crypto import Random
         from Crypto.Cipher import AES
         from pskc.crypto import pad
-        iv = iv or Random.get_random_bytes(AES.block_size)
+        iv = iv or os.urandom(AES.block_size)
         cipher = AES.new(key, AES.MODE_CBC, iv)
         return iv + cipher.encrypt(pad(plaintext, AES.block_size))
     elif algorithm.endswith('#tripledes-cbc'):
-        from Crypto import Random
         from Crypto.Cipher import DES3
         from pskc.crypto import pad
-        iv = iv or Random.get_random_bytes(DES3.block_size)
+        iv = iv or os.urandom(DES3.block_size)
         cipher = DES3.new(key, DES3.MODE_CBC, iv)
         return iv + cipher.encrypt(pad(plaintext, DES3.block_size))
     elif algorithm.endswith('#kw-aes128') or \
@@ -195,10 +195,9 @@ class KeyDerivation(object):
 
     def setup_pbkdf2(self, password, salt=None, salt_length=16,
                      key_length=None, iterations=None, prf=None):
-        from Crypto import Random
         self.algorithm = 'pbkdf2'
         if salt is None:
-            salt = Random.get_random_bytes(salt_length)
+            salt = os.urandom(salt_length)
         self.pbkdf2_salt = salt
         if iterations:
             self.pbkdf2_iterations = iterations
@@ -298,9 +297,8 @@ class Encryption(object):
         self._setup_encryption(kwargs)
         self.key = kwargs.pop('key', self.key)
         if not self.key:
-            from Crypto import Random
-            self.key = Random.get_random_bytes(kwargs.pop(
-                'key_length', self.algorithm_key_lengths[-1]))
+            self.key = os.urandom(
+                kwargs.pop('key_length', self.algorithm_key_lengths[-1]))
 
     def setup_pbkdf2(self, password, **kwargs):
         """Configure password-based PSKC encryption when writing the file.
diff --git a/pskc/mac.py b/pskc/mac.py
index 5ac1a92..5f258d0 100644
--- a/pskc/mac.py
+++ b/pskc/mac.py
@@ -29,6 +29,7 @@ with the PSKC encryption key.
 """
 
 
+import os
 import re
 
 
@@ -145,5 +146,4 @@ class MAC(object):
             self.algorithm = 'hmac-sha1'
         # generate an HMAC key
         if not self.key:
-            from Crypto import Random
-            self.key = Random.get_random_bytes(self.algorithm_key_length)
+            self.key = os.urandom(self.algorithm_key_length)
diff --git a/tests/test_aeskw.doctest b/tests/test_aeskw.doctest
index c6ca659..a238a7f 100644
--- a/tests/test_aeskw.doctest
+++ b/tests/test_aeskw.doctest
@@ -1,6 +1,6 @@
 test_keywrap.doctest - test keywrap functions
 
-Copyright (C) 2014-2016 Arthur de Jong
+Copyright (C) 2014-2017 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
@@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 02110-1301 USA
 
 
->>> from Crypto.Util.number import long_to_bytes
+>>> import struct
 >>> from binascii import a2b_hex
 >>> from pskc.crypto.aeskw import wrap, unwrap
 
@@ -210,7 +210,7 @@ works because we have padded the plaintext with two 0 bytes.
 
 >>> key = a2b_hex('5840df6e29b02af1ab493b705bf16ea1ae8338f4dcc176a8')
 >>> plaintext = a2b_hex('c37b7e6492584340bed1220765460000')
->>> iv = a2b_hex('a65959a6') + long_to_bytes(14, 4)
+>>> iv = a2b_hex('a65959a6') + struct.pack('>I', 14)
 >>> ciphertext = wrap(plaintext, key, iv=iv)
 >>> unwrap(ciphertext, key, iv=iv) == plaintext
 True
@@ -221,7 +221,7 @@ True
 If we mangle the IV to look like an RFC 5649 value but with an invalid
 padding length we should get an exception.
 
->>> iv = a2b_hex('a65959a6') + long_to_bytes(12, 4)
+>>> iv = a2b_hex('a65959a6') + struct.pack('>I', 12)
 >>> ciphertext = wrap(plaintext, key, iv=iv)
 >>> unwrap(ciphertext, key, iv=iv) == plaintext
 True
diff --git a/tests/test_write.doctest b/tests/test_write.doctest
index 890c99b..8e847f3 100644
--- a/tests/test_write.doctest
+++ b/tests/test_write.doctest
@@ -21,9 +21,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 >>> from pskc import PSKC
 
 >>> import datetime
+>>> import os
 >>> import sys
 >>> import tempfile
->>> from Crypto import Random
 >>> from binascii import a2b_hex
 >>> from dateutil.tz import tzutc
 
@@ -412,7 +412,7 @@ decryption combinations.
 >>> def test_algorithm(algorithm):
 ...     f = tempfile.NamedTemporaryFile()
 ...     pskc1 = PSKC()
-...     pskc1.add_key(secret=Random.get_random_bytes(16))
+...     pskc1.add_key(secret=os.urandom(16))
 ...     pskc1.encryption.setup_preshared_key(algorithm=algorithm)
 ...     pskc1.write(f.name)
 ...     pskc2 = PSKC(f.name)

https://arthurdejong.org/git/python-pskc/commit/?id=d0eddf88287bce398cb7a55ee20b384e3f764a4d

commit d0eddf88287bce398cb7a55ee20b384e3f764a4d
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sun Sep 24 21:32:55 2017 +0200

    Implement our own XML formatting
    
    This avoids a using xml.dom.minidom to indent the XML tree and keep the
    attributes ordered alphabetically. This also allows for customisations
    to the XML formatting.

diff --git a/pskc/serialiser.py b/pskc/serialiser.py
index 2dc82d4..6c9a5ab 100644
--- a/pskc/serialiser.py
+++ b/pskc/serialiser.py
@@ -30,7 +30,7 @@ class PSKCSerialiser(object):
 
     @classmethod
     def serialise_file(cls, pskc, output):
-        xml = tostring(cls.serialise_document(pskc)) + '\n'.encode('UTF-8')
+        xml = tostring(cls.serialise_document(pskc))
         try:
             output.write(xml)
         except TypeError:  # pragma: no cover (Python 3 specific)
diff --git a/pskc/xml.py b/pskc/xml.py
index bc10c81..56f45bc 100644
--- a/pskc/xml.py
+++ b/pskc/xml.py
@@ -25,6 +25,8 @@ This module provides some utility functions for parsing XML 
files.
 
 from __future__ import absolute_import
 
+from collections import OrderedDict
+
 # try to find a usable ElementTree implementation
 try:  # pragma: no cover (different implementations)
     from lxml.etree import parse as xml_parse, tostring as xml_tostring
@@ -176,9 +178,9 @@ def mk_elem(parent, tag=None, text=None, empty=False, 
**kwargs):
         ns, name = tag.split(':', 1)
         tag = '{%s}%s' % (namespaces[ns], name)
     if parent is None:
-        element = Element(tag)
+        element = Element(tag, OrderedDict())
     else:
-        element = SubElement(parent, tag)
+        element = SubElement(parent, tag, OrderedDict())
     # set text of element
     if text is not None:
         element.text = _format(text)
@@ -189,9 +191,28 @@ def mk_elem(parent, tag=None, text=None, empty=False, 
**kwargs):
     return element
 
 
+def reformat(element, indent=''):
+    """Reformat the XML tree to have nice wrapping and indenting."""
+    # re-order attributes by alphabet
+    attrib = sorted(element.attrib.items())
+    element.attrib.clear()
+    element.attrib.update(attrib)
+    if len(element) == 0:
+        # clean up inner text
+        if element.text:
+            element.text = element.text.strip()
+    else:
+        # indent children
+        element.text = '\n ' + indent
+        childred = list(element)
+        for child in childred:
+            reformat(child, indent + ' ')
+        childred[-1].tail = '\n' + indent
+    element.tail = '\n' + indent
+
+
 def tostring(element):
     """Return a serialised XML document for the element tree."""
-    from xml.dom import minidom
     # if we are using lxml.etree move namespaces to toplevel element
     if hasattr(element, 'nsmap'):  # pragma: no cover (only on lxml)
         # get all used namespaces
@@ -203,6 +224,11 @@ def tostring(element):
         for a in element:
             e.append(a)
         element = e
+    reformat(element)
     xml = xml_tostring(element, encoding='UTF-8')
-    return minidom.parseString(xml).toprettyxml(
-        indent=' ', encoding='UTF-8').strip()
+    xml_decl = b"<?xml version='1.0' encoding='UTF-8'?>\n"
+    if xml.startswith(xml_decl):  # pragma: no cover (only a few cases)
+        xml = xml[len(xml_decl):]
+    return (
+        b'<?xml version="1.0" encoding="UTF-8"?>\n' +
+        xml.replace(b' />', b'/>').strip() + b'\n')
diff --git a/tests/test_write.doctest b/tests/test_write.doctest
index 1bcad0f..890c99b 100644
--- a/tests/test_write.doctest
+++ b/tests/test_write.doctest
@@ -103,7 +103,7 @@ argument).
 >>> with open(f.name, 'r') as r:
 ...     x = sys.stdout.write(r.read())  #doctest: +REPORT_UDIFF
 <?xml version="1.0" encoding="UTF-8"?>
-<pskc:KeyContainer Version="1.0" 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc">
+<pskc:KeyContainer xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
Version="1.0">
  <pskc:KeyPackage>
   <pskc:DeviceInfo>
    <pskc:Manufacturer>Manufacturer</pskc:Manufacturer>
@@ -197,7 +197,7 @@ Read an encrypted PSKC file and write it out as an 
unencrypted file.
 >>> pskc.encryption.key_name = None
 >>> pskc.write(sys.stdout)  #doctest: +REPORT_UDIFF
 <?xml version="1.0" encoding="UTF-8"?>
-<pskc:KeyContainer Version="1.0" 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc">
+<pskc:KeyContainer xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
Version="1.0">
  <pskc:KeyPackage>
   <pskc:Key>
    <pskc:Data>
@@ -216,7 +216,7 @@ providing the encryption key.
 >>> pskc = PSKC('tests/rfc6030/figure6.pskcxml')
 >>> pskc.write(sys.stdout)  #doctest: +REPORT_UDIFF
 <?xml version="1.0" encoding="UTF-8"?>
-<pskc:KeyContainer Version="1.0" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"; 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#";>
+<pskc:KeyContainer xmlns:ds="http://www.w3.org/2000/09/xmldsig#"; 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"; Version="1.0">
  <pskc:EncryptionKey>
   <ds:KeyName>Pre-shared-key</ds:KeyName>
  </pskc:EncryptionKey>
@@ -274,7 +274,7 @@ Set up an encrypted PSKC file and generate a pre-shared key 
for it.
 >>> with open(f.name, 'r') as r:
 ...     x = sys.stdout.write(r.read())  #doctest: +ELLIPSIS +REPORT_UDIFF
 <?xml version="1.0" encoding="UTF-8"?>
-<pskc:KeyContainer Version="1.0" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"; 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#";>
+<pskc:KeyContainer xmlns:ds="http://www.w3.org/2000/09/xmldsig#"; 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"; Version="1.0">
  <pskc:EncryptionKey>
   <ds:KeyName>Pre-shared KEY</ds:KeyName>
  </pskc:EncryptionKey>
@@ -343,7 +343,7 @@ Use PBKDF2 to derive a key instead of using a pre-shared 
key.
 >>> with open(f.name, 'r') as r:
 ...     x = sys.stdout.write(r.read())  #doctest: +ELLIPSIS +REPORT_UDIFF
 <?xml version="1.0" encoding="UTF-8"?>
-<pskc:KeyContainer Version="1.0" 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"; 
xmlns:xenc11="http://www.w3.org/2009/xmlenc11#";>
+<pskc:KeyContainer xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"; 
xmlns:xenc11="http://www.w3.org/2009/xmlenc11#"; Version="1.0">
  <pskc:EncryptionKey>
   <xenc11:DerivedKey>
    <xenc11:KeyDerivationMethod 
Algorithm="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2";>
@@ -488,7 +488,7 @@ result in an empty KeyDerivation element.
 >>> pskc.encryption.derivation.algorithm = 'unknown'
 >>> pskc.write(sys.stdout)  #doctest: +ELLIPSIS +REPORT_UDIFF
 <?xml version="1.0" encoding="UTF-8"?>
-<pskc:KeyContainer Version="1.0" 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"; 
xmlns:xenc11="http://www.w3.org/2009/xmlenc11#";>
+<pskc:KeyContainer xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"; 
xmlns:xenc11="http://www.w3.org/2009/xmlenc11#"; Version="1.0">
  <pskc:EncryptionKey>
   <xenc11:DerivedKey>
    <xenc11:KeyDerivationMethod Algorithm="unknown"/>
@@ -511,7 +511,7 @@ We can make the PKKDF2 salt have to be transmitted 
out-of-bounds:
 >>> pskc.encryption.derivation.pbkdf2_salt = None
 >>> pskc.write(sys.stdout)  #doctest: +ELLIPSIS +REPORT_UDIFF
 <?xml version="1.0" encoding="UTF-8"?>
-<pskc:KeyContainer Version="1.0" 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"; 
xmlns:xenc11="http://www.w3.org/2009/xmlenc11#";>
+<pskc:KeyContainer xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"; 
xmlns:xenc11="http://www.w3.org/2009/xmlenc11#"; Version="1.0">
  <pskc:EncryptionKey>
   <xenc11:DerivedKey>
    <xenc11:KeyDerivationMethod 
Algorithm="http://www.rsasecurity.com/rsalabs/pkcs/schemas/pkcs-5v2-0#pbkdf2";>
@@ -541,7 +541,7 @@ set on one key end up being applied to both keys.
 >>> key = device.add_key(id='pin0', secret='5678')
 >>> pskc.write(sys.stdout)  #doctest: +ELLIPSIS +REPORT_UDIFF
 <?xml version="1.0" encoding="UTF-8"?>
-<pskc:KeyContainer Version="1.0" 
xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc">
+<pskc:KeyContainer xmlns:pskc="urn:ietf:params:xml:ns:keyprov:pskc" 
Version="1.0">
  <pskc:KeyPackage>
   <pskc:DeviceInfo>
    <pskc:Manufacturer>TokenVendorAcme</pskc:Manufacturer>

-----------------------------------------------------------------------

Summary of changes:
 pskc/crypto/aeskw.py       | 21 ++++++++++++++-------
 pskc/crypto/tripledeskw.py |  6 +++---
 pskc/encryption.py         | 20 +++++++++-----------
 pskc/mac.py                |  4 ++--
 pskc/serialiser.py         |  2 +-
 pskc/xml.py                | 36 +++++++++++++++++++++++++++++++-----
 tests/test_aeskw.doctest   |  8 ++++----
 tests/test_write.doctest   | 20 ++++++++++----------
 8 files changed, 74 insertions(+), 43 deletions(-)


hooks/post-receive
-- 
python-pskc
-- 
To unsubscribe send an email to
python-pskc-commits-unsubscribe@lists.arthurdejong.org or see
https://lists.arthurdejong.org/python-pskc-commits/