lists.arthurdejong.org
RSS feed

python-pskc branch master updated. 0.5-22-g25cb2fc

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

python-pskc branch master updated. 0.5-22-g25cb2fc



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  25cb2fc2e55147ad6b7a4a8a0754d972448a13a9 (commit)
       via  225e569d8c10be1bf747f585507869c6aaa72865 (commit)
       via  5dff7d4b44751df5768b99d74cbd7a977edd3a17 (commit)
      from  2c8a9b7b1b87a3cc07aac800d77db4652165d3e1 (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=25cb2fc2e55147ad6b7a4a8a0754d972448a13a9

commit 25cb2fc2e55147ad6b7a4a8a0754d972448a13a9
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Mon Oct 9 21:33:07 2017 +0200

    Ignore missing docstring in __init__ in flake

diff --git a/setup.cfg b/setup.cfg
index 730bb60..0c042e0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -19,6 +19,6 @@ cover-min-percentage=100
 all_files  = 1
 
 [flake8]
-ignore = D101,D102,D105,D202,D205,D209,D400,N806
+ignore = D101,D102,D105,D107,D202,D205,D209,D400,N806
 max-complexity = 14
 max-line-length = 78

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

commit 225e569d8c10be1bf747f585507869c6aaa72865
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat Sep 30 23:55:42 2017 +0200

    Replace pycrypto with cryptography
    
    The cryptography library is better supported.
    
    This uses the functions from cryptography for AES and Triple DES
    encryption, replaces the (un)padding functions that were previously
    implemented in python-pskc with cryptography and uses PBKDF2
    implementation from hashlib.

diff --git a/pskc/crypto/__init__.py b/pskc/crypto/__init__.py
index 356e498..ac96d67 100644
--- a/pskc/crypto/__init__.py
+++ b/pskc/crypto/__init__.py
@@ -1,7 +1,7 @@
 # __init__.py - general crypto utility functions
 # coding: utf-8
 #
-# Copyright (C) 2016 Arthur de Jong
+# Copyright (C) 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
@@ -19,20 +19,3 @@
 # 02110-1301 USA
 
 """Implement crypto utility functions."""
-
-
-def pad(value, block_size):
-    """Pad the value to block_size length."""
-    padding = block_size - (len(value) % block_size)
-    return value + padding * chr(padding).encode('ascii')
-
-
-def unpad(value, block_size):
-    """Remove padding from the plaintext."""
-    from pskc.exceptions import DecryptionError
-    padding = ord(value[-1:])
-    # only unpad if all padding bytes are the same
-    if (padding > 0 and padding <= block_size and
-            value[-padding:] == padding * chr(padding).encode('ascii')):
-        return value[:-padding]
-    raise DecryptionError('Invalid padding')
diff --git a/pskc/crypto/aeskw.py b/pskc/crypto/aeskw.py
index 7396f26..84e6f85 100644
--- a/pskc/crypto/aeskw.py
+++ b/pskc/crypto/aeskw.py
@@ -23,7 +23,8 @@
 import binascii
 import struct
 
-from Crypto.Cipher import AES
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 
 from pskc.exceptions import DecryptionError, EncryptionError
 
@@ -71,19 +72,20 @@ def wrap(plaintext, key, iv=None, pad=None):
         else:
             iv = RFC3394_IV
 
-    encrypt = AES.new(key).encrypt
+    cipher = Cipher(algorithms.AES(key), modes.ECB(), default_backend())
+    encryptor = cipher.encryptor()
     n = len(plaintext) // 8
 
     if n == 1:
         # RFC 5649 shortcut
-        return encrypt(iv + plaintext)
+        return encryptor.update(iv + plaintext)
 
     A = iv
     R = [plaintext[i * 8:i * 8 + 8]
          for i in range(n)]
     for j in range(6):
         for i in range(n):
-            A, R[i] = _split(encrypt(A + R[i]))
+            A, R[i] = _split(encryptor.update(A + R[i]))
             A = _strxor(A, struct.pack('>Q', n * j + i + 1))
     return A + b''.join(R)
 
@@ -103,11 +105,12 @@ def unwrap(ciphertext, key, iv=None, pad=None):
     if len(ciphertext) % 8 != 0 or (pad is False and len(ciphertext) < 24):
         raise DecryptionError('Ciphertext length wrong')
 
-    decrypt = AES.new(key).decrypt
+    cipher = Cipher(algorithms.AES(key), modes.ECB(), default_backend())
+    decryptor = cipher.decryptor()
     n = len(ciphertext) // 8 - 1
 
     if n == 1:
-        A, plaintext = _split(decrypt(ciphertext))
+        A, plaintext = _split(decryptor.update(ciphertext))
     else:
         A = ciphertext[:8]
         R = [ciphertext[(i + 1) * 8:(i + 2) * 8]
@@ -115,7 +118,7 @@ def unwrap(ciphertext, key, iv=None, pad=None):
         for j in reversed(range(6)):
             for i in reversed(range(n)):
                 A = _strxor(A, struct.pack('>Q', n * j + i + 1))
-                A, R[i] = _split(decrypt(A + R[i]))
+                A, R[i] = _split(decryptor.update(A + R[i]))
         plaintext = b''.join(R)
 
     if iv is None:
diff --git a/pskc/crypto/tripledeskw.py b/pskc/crypto/tripledeskw.py
index 9494026..0314fc9 100644
--- a/pskc/crypto/tripledeskw.py
+++ b/pskc/crypto/tripledeskw.py
@@ -21,17 +21,18 @@
 """Implement Triple DES key wrapping as described in RFC 3217."""
 
 import binascii
+import hashlib
 import os
 
-from Crypto.Cipher import DES3
-from Crypto.Hash import SHA
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
 
 from pskc.exceptions import DecryptionError, EncryptionError
 
 
 def _cms_hash(value):
     """Return the key hash algorithm described in RFC 3217 section 2."""
-    return SHA.new(value).digest()[:8]
+    return hashlib.sha1(value).digest()[:8]
 
 
 RFC3217_IV = binascii.a2b_hex('4adda22c79e82105')
@@ -44,14 +45,19 @@ def wrap(plaintext, key, iv=None):
     to wrap) using the provided key. If the iv is None, it is randomly
     generated.
     """
-    if len(plaintext) % DES3.block_size != 0:
+    if 8 * len(plaintext) % algorithms.TripleDES.block_size != 0:
         raise EncryptionError('Plaintext length wrong')
     if iv is None:
         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)
-    return cipher.encrypt(tmp[::-1])
+    backend = default_backend()
+    cipher = Cipher(algorithms.TripleDES(key), modes.CBC(iv), backend)
+    encryptor = cipher.encryptor()
+    tmp = (
+        iv + encryptor.update(plaintext + _cms_hash(plaintext)) +
+        encryptor.finalize())
+    cipher = Cipher(algorithms.TripleDES(key), modes.CBC(RFC3217_IV), backend)
+    encryptor = cipher.encryptor()
+    return encryptor.update(tmp[::-1]) + encryptor.finalize()
 
 
 def unwrap(ciphertext, key):
@@ -60,12 +66,15 @@ def unwrap(ciphertext, key):
     This uses the algorithm from RFC 3217 to decrypt the ciphertext (the
     previously wrapped key) using the provided key.
     """
-    if len(ciphertext) % DES3.block_size != 0:
+    if 8 * len(ciphertext) % algorithms.TripleDES.block_size != 0:
         raise DecryptionError('Ciphertext length wrong')
-    cipher = DES3.new(key, DES3.MODE_CBC, RFC3217_IV)
-    tmp = cipher.decrypt(ciphertext)[::-1]
-    cipher = DES3.new(key, DES3.MODE_CBC, tmp[:8])
-    tmp = cipher.decrypt(tmp[8:])
+    backend = default_backend()
+    cipher = Cipher(algorithms.TripleDES(key), modes.CBC(RFC3217_IV), backend)
+    decryptor = cipher.decryptor()
+    tmp = (decryptor.update(ciphertext) + decryptor.finalize())[::-1]
+    cipher = Cipher(algorithms.TripleDES(key), modes.CBC(tmp[:8]), backend)
+    decryptor = cipher.decryptor()
+    tmp = decryptor.update(tmp[8:]) + decryptor.finalize()
     if tmp[-8:] == _cms_hash(tmp[:-8]):
         return tmp[:-8]
     raise DecryptionError('CMS key checksum error')
diff --git a/pskc/encryption.py b/pskc/encryption.py
index 3ba22c3..d90476c 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -51,8 +51,30 @@ def algorithm_key_lengths(algorithm):
         raise DecryptionError('Unsupported algorithm: %r' % algorithm)
 
 
+def _decrypt_cbc(algorithm, key, ciphertext, iv=None):
+    """Decrypt the ciphertext and return the plaintext value."""
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives import padding
+    from cryptography.hazmat.primitives.ciphers import Cipher, modes
+    from pskc.exceptions import DecryptionError
+    if not iv:
+        iv = ciphertext[:algorithm.block_size // 8]
+        ciphertext = ciphertext[algorithm.block_size // 8:]
+    cipher = Cipher(
+        algorithm(key), modes.CBC(iv), backend=default_backend())
+    decryptor = cipher.decryptor()
+    unpadder = padding.PKCS7(algorithm.block_size).unpadder()
+    try:
+        return unpadder.update(
+            decryptor.update(ciphertext) +
+            decryptor.finalize()) + unpadder.finalize()
+    except ValueError:
+        raise DecryptionError('Invalid padding')
+
+
 def decrypt(algorithm, key, ciphertext, iv=None):
     """Decrypt the ciphertext and return the plaintext value."""
+    from cryptography.hazmat.primitives.ciphers import algorithms
     from pskc.exceptions import DecryptionError
     if key is None:
         raise DecryptionError('No key available')
@@ -63,21 +85,9 @@ def decrypt(algorithm, key, ciphertext, iv=None):
     if algorithm.endswith('#aes128-cbc') or \
             algorithm.endswith('#aes192-cbc') or \
             algorithm.endswith('#aes256-cbc'):
-        from Crypto.Cipher import AES
-        from pskc.crypto import unpad
-        if not iv:
-            iv = ciphertext[:AES.block_size]
-            ciphertext = ciphertext[AES.block_size:]
-        cipher = AES.new(key, AES.MODE_CBC, iv)
-        return unpad(cipher.decrypt(ciphertext), AES.block_size)
+        return _decrypt_cbc(algorithms.AES, key, ciphertext, iv)
     elif algorithm.endswith('#tripledes-cbc'):
-        from Crypto.Cipher import DES3
-        from pskc.crypto import unpad
-        if not iv:
-            iv = ciphertext[:DES3.block_size]
-            ciphertext = ciphertext[DES3.block_size:]
-        cipher = DES3.new(key, DES3.MODE_CBC, iv)
-        return unpad(cipher.decrypt(ciphertext), DES3.block_size)
+        return _decrypt_cbc(algorithms.TripleDES, key, ciphertext, iv)
     elif algorithm.endswith('#kw-aes128') or \
             algorithm.endswith('#kw-aes192') or \
             algorithm.endswith('#kw-aes256'):
@@ -89,8 +99,25 @@ def decrypt(algorithm, key, ciphertext, iv=None):
     # no fallthrough because algorithm_key_lengths() fails with unknown algo
 
 
+def _encrypt_cbc(algorithm, key, plaintext, iv=None):
+    """Encrypt the provided value with the key using the algorithm."""
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives import padding
+    from cryptography.hazmat.primitives.ciphers import Cipher, modes
+    iv = iv or os.urandom(algorithm.block_size // 8)
+    cipher = Cipher(
+        algorithm(key), modes.CBC(iv), backend=default_backend())
+    encryptor = cipher.encryptor()
+    padder = padding.PKCS7(algorithm.block_size).padder()
+    return (
+        iv + encryptor.update(
+            padder.update(plaintext) + padder.finalize()) +
+        encryptor.finalize())
+
+
 def encrypt(algorithm, key, plaintext, iv=None):
     """Encrypt the provided value with the key using the algorithm."""
+    from cryptography.hazmat.primitives.ciphers import algorithms
     from pskc.exceptions import EncryptionError
     if key is None:
         raise EncryptionError('No key available')
@@ -101,17 +128,9 @@ def encrypt(algorithm, key, plaintext, iv=None):
     if algorithm.endswith('#aes128-cbc') or \
             algorithm.endswith('#aes192-cbc') or \
             algorithm.endswith('#aes256-cbc'):
-        from Crypto.Cipher import AES
-        from pskc.crypto import pad
-        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))
+        return _encrypt_cbc(algorithms.AES, key, plaintext, iv)
     elif algorithm.endswith('#tripledes-cbc'):
-        from Crypto.Cipher import DES3
-        from pskc.crypto import pad
-        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))
+        return _encrypt_cbc(algorithms.TripleDES, key, plaintext, iv)
     elif algorithm.endswith('#kw-aes128') or \
             algorithm.endswith('#kw-aes192') or \
             algorithm.endswith('#kw-aes256'):
diff --git a/pskc/mac.py b/pskc/mac.py
index 5f258d0..65ec9b3 100644
--- a/pskc/mac.py
+++ b/pskc/mac.py
@@ -50,20 +50,14 @@ def _get_hash_obj(algorithm, *args):
     raise DecryptionError('Unsupported MAC algorithm: %r' % algorithm)
 
 
-def get_mac_fn(algorithm):
-    """Return a function that takes a key and a value and returns an HMAC for
-    the specified algorithm."""
+def mac(algorithm, key, value):
+    """Generate the MAC value over the specified value."""
     import hmac
-    return lambda key, value: hmac.new(
+    return hmac.new(
         key, value,
         lambda *args: _get_hash_obj(algorithm, *args)).digest()
 
 
-def mac(algorithm, key, value):
-    """Generate the MAC value over the specified value."""
-    return get_mac_fn(algorithm)(key, value)
-
-
 def mac_key_length(algorithm):
     """Recommended minimal key length in bytes for the set algorithm."""
     # https://tools.ietf.org/html/rfc2104#section-3
diff --git a/setup.cfg b/setup.cfg
index 9f121ef..730bb60 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -20,5 +20,5 @@ all_files  = 1
 
 [flake8]
 ignore = D101,D102,D105,D202,D205,D209,D400,N806
-max-complexity = 12
+max-complexity = 14
 max-line-length = 78
diff --git a/setup.py b/setup.py
index 7670ec8..2472d7f 100755
--- a/setup.py
+++ b/setup.py
@@ -62,7 +62,7 @@ setup(
         'Topic :: Text Processing :: Markup :: XML',
     ],
     packages=find_packages(),
-    install_requires=['pycrypto', 'python-dateutil'],
+    install_requires=['cryptography', 'python-dateutil'],
     extras_require={
         'lxml':  ['lxml'],
         'defuse':  ['defusedxml'],
diff --git a/tests/test_crypto.doctest b/tests/test_crypto.doctest
deleted file mode 100644
index 73cef48..0000000
--- a/tests/test_crypto.doctest
+++ /dev/null
@@ -1,76 +0,0 @@
-test_crypto.doctest - test various internal crypto helper functions
-
-Copyright (C) 2016 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
-License as published by the Free Software Foundation; either
-version 2.1 of the License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301 USA
-
->>> import random
->>> from binascii import a2b_hex, b2a_hex
->>> def tostr(x):
-...     return str(x.decode())
->>> def decode(f):
-...     return lambda x: tostr(f(x))
->>> b2a_hex = decode(b2a_hex)
-
->>> from pskc.crypto import pad, unpad
-
-
-Tests for padding and unpadding. This should follow PKCS#7 as defined in RFC
-5652 section 6.3.
-
->>> b2a_hex(pad(a2b_hex('1234'), 16))
-'12340e0e0e0e0e0e0e0e0e0e0e0e0e0e'
->>> b2a_hex(pad(a2b_hex('abcdefabcdefabcdefabcdefabcdef'), 16))
-'abcdefabcdefabcdefabcdefabcdef01'
->>> b2a_hex(pad(bytearray(16 * [65]), 16))
-'4141414141414141414141414141414110101010101010101010101010101010'
-
->>> b2a_hex(unpad(a2b_hex('12340e0e0e0e0e0e0e0e0e0e0e0e0e0e'), 16))
-'1234'
->>> b2a_hex(unpad(a2b_hex('abcdefabcdefabcdefabcdefabcdef01'), 16))
-'abcdefabcdefabcdefabcdefabcdef'
-
-
-Padding should always work but there are some conditions where unpadding does
-not work. Specifically, the to be removed padding should all have the same
-bytes:
-
->>> b2a_hex(unpad(a2b_hex('ffff0606060f0606'), 8))  # doctest: 
+IGNORE_EXCEPTION_DETAIL
-Traceback (most recent call last):
-    ...
-DecryptionError: Invalid padding
-
-The unpadding should also refuse to remove more than a block size worth of
-data:
-
->>> b2a_hex(unpad(a2b_hex('ffff0e0e0e0e0e0e0e0e0e0e0e0e0e0e'), 8))  # doctest: 
+IGNORE_EXCEPTION_DETAIL
-Traceback (most recent call last):
-    ...
-DecryptionError: Invalid padding
-
-
-We generate a number of (non-cryptographically strong) random strings and
-ensure that padding and unpadding works for them.
-
->>> for i in range(100):
-...     l = random.randrange(1, 255)
-...     r = bytearray(random.getrandbits(8) for x in range(l))
-...     b = random.randrange(10, 100)
-...     p = pad(r, b)
-...     u = unpad(p, b)
-...     if u != r:
-...         raise ValueError('%s -> %s -> %s (%d)' % (
-...             b2a_hex(r), b2a_hex(p), b2a_hex(u), b))
diff --git a/tests/test_encryption.doctest b/tests/test_encryption.doctest
index fa567ef..7e1d443 100644
--- a/tests/test_encryption.doctest
+++ b/tests/test_encryption.doctest
@@ -1,6 +1,6 @@
 test_encryption.doctest - test various encryption schemes
 
-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
@@ -77,6 +77,11 @@ DecryptionError: Invalid key length
 Traceback (most recent call last):
     ...
 DecryptionError: Invalid key length
+>>> pskc.encryption.key = a2b_hex('11111111111111111111111111111111')
+>>> pskc.keys[0].secret  # doctest: +IGNORE_EXCEPTION_DETAIL
+Traceback (most recent call last):
+    ...
+DecryptionError: Invalid padding
 >>> pskc.encryption.key = a2b_hex('12345678901234567890123456789012')
 >>> tostr(pskc.keys[0].secret)
 '12345678901234567890'
diff --git a/tox.ini b/tox.ini
index fd591cf..731fcc6 100644
--- a/tox.ini
+++ b/tox.ini
@@ -5,8 +5,6 @@ skip_missing_interpreters = True
 [testenv]
 deps = nose
        coverage
-       pycrypto
-       python-dateutil
        lxml: lxml
        defusedxml: defusedxml
 commands = nosetests

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

commit 5dff7d4b44751df5768b99d74cbd7a977edd3a17
Author: Arthur de Jong <arthur@arthurdejong.org>
Date:   Sat Sep 30 21:31:48 2017 +0200

    Use PBKDF2 from hashlib
    
    This uses pbkdf2_hmac() from hashlib for the PBKDF2 calculation.
    The downside of this is that this function is only available since
    Python 2.7.8.

diff --git a/pskc/encryption.py b/pskc/encryption.py
index 5ae31db..3ba22c3 100644
--- a/pskc/encryption.py
+++ b/pskc/encryption.py
@@ -28,6 +28,7 @@ The encryption key can be derived using the KeyDerivation 
class.
 
 
 import os
+import re
 
 
 def algorithm_key_lengths(algorithm):
@@ -165,20 +166,28 @@ class KeyDerivation(object):
         self._pbkdf2_prf = normalise_algorithm(value)
 
     def derive_pbkdf2(self, password):
-        from Crypto.Protocol.KDF import PBKDF2
-        from pskc.mac import get_mac_fn
-        from pskc.exceptions import DecryptionError, KeyDerivationError
-        prf = None
+        from hashlib import pbkdf2_hmac
+        from pskc.exceptions import KeyDerivationError
+        prf = 'sha1'
         if self.pbkdf2_prf:
-            prf = get_mac_fn(self.pbkdf2_prf)
+            match = re.search(
+                r'^(.*#)?hmac-(?P<hash>[a-z0-9-]+)$', self.pbkdf2_prf)
+            if match:
+                prf = match.group('hash')
+            else:
+                raise KeyDerivationError(
+                    'Unsupported PRF: %r' % self.pbkdf2_prf)
         if not all((password, self.pbkdf2_salt, self.pbkdf2_key_length,
                     self.pbkdf2_iterations)):
             raise KeyDerivationError('Incomplete PBKDF2 configuration')
+        # force conversion to bytestring on Python 3
+        if not isinstance(password, type(b'')):
+            password = password.encode()  # pragma: no cover (Py3 specific)
         try:
-            return PBKDF2(
-                password, self.pbkdf2_salt, dkLen=self.pbkdf2_key_length,
-                count=self.pbkdf2_iterations, prf=prf)
-        except DecryptionError:
+            return pbkdf2_hmac(
+                prf, password, self.pbkdf2_salt, self.pbkdf2_iterations,
+                self.pbkdf2_key_length)
+        except ValueError:
             raise KeyDerivationError(
                 'Pseudorandom function unsupported: %r' % self.pbkdf2_prf)
 

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

Summary of changes:
 pskc/crypto/__init__.py       | 19 +--------
 pskc/crypto/aeskw.py          | 17 ++++----
 pskc/crypto/tripledeskw.py    | 35 ++++++++++------
 pskc/encryption.py            | 94 ++++++++++++++++++++++++++++---------------
 pskc/mac.py                   | 12 ++----
 setup.cfg                     |  4 +-
 setup.py                      |  2 +-
 tests/test_crypto.doctest     | 76 ----------------------------------
 tests/test_encryption.doctest |  7 +++-
 tox.ini                       |  2 -
 10 files changed, 106 insertions(+), 162 deletions(-)
 delete mode 100644 tests/test_crypto.doctest


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/