rework encryption unittests
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Tue, 18 Apr 2017 13:04:07 +0000 (15:04 +0200)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Mon, 2 Apr 2018 11:34:08 +0000 (13:34 +0200)
deltatar/crypto.py
testing/__init__.py
testing/test_encryption.py

index 5460dec..5fa4048 100755 (executable)
@@ -676,7 +676,7 @@ class Decrypt (Crypto):
             # this won’t catch malformed specs though
             raise InvalidParameter ("next: wrong type of parameter hdr: "
                                     "expected bytes or spec, got %s"
-                                    % type (tag))
+                                    % type (hdr))
         try:
             paramversion = hdr ["paramversion"]
             nacl         = hdr ["nacl"]
index 527c751..1553324 100644 (file)
 import os, unittest, hashlib, string
 import random
 
-def new_volume_handler(tarobj, base_name, volume_number):
+import sys
+
+def new_volume_handler(tarobj, base_name, volume_number, encryption=None):
     '''
     Handles the new volumes
     '''
     volume_path = "%s.%d" % (base_name, volume_number)
-    tarobj.open_volume(volume_path)
+    tarobj.open_volume(volume_path, encryption=encryption)
+
+def make_new_encryption_volume_handler(encryption):
+    '''
+    Handles the new volumes using the right crypto context.
+    '''
+    return lambda tarobj, base_name, volume_number: \
+        new_volume_handler (tarobj, base_name, volume_number,
+                            encryption=encryption)
 
 def closing_new_volume_handler(tarobj, base_name, volume_number):
     '''
index 432acdf..5088ab2 100644 (file)
 
 import os
 
+from deltatar import crypto
 from deltatar.tarfile import TarFile, GNU_FORMAT
 
 import filesplit
 from . import BaseTest
-from . import new_volume_handler
+from . import new_volume_handler, make_new_encryption_volume_handler
 
+DELTATAR_HEADER_VERSION    = 1
+DELTATAR_PARAMETER_VERSION = 1
 
 class EncryptionTest(BaseTest):
     """
     Test encryption after compression in tarfiles
     """
 
-    def test_openssl_decrypt(self):
+    def test_cli_decrypt(self):
         """
         Create a tar file with only one file inside, using concat
-        compression and encryption mode. Then decrypt with openssl,
+        compression and encryption mode. Then decrypt with crypto.py,
         decompress it with zcat and untar it with gnu tar.
+
+        Note that in an earlier implementation of the Deltatar crypto
+        layer, files could be decrypted directly using OpenSSL command
+        line tools. With the version 1 parameters this is no longer
+        possible since OpenSSL does not ship with a command line tool
+        that understands GCM and even if it did, it would be very
+        unlikely that it could be made with the raw pdtcrypt format.
+
+        Thus, we rely on the functionality of our encryption library
+        ``crypto.py`` to decrypt on the command line; the rest of the
+        data pipeline (→ gzip → tar → files) remains the same.
         """
 
         # create the content of the file to compress and hash it
         hash = self.create_file("big", 50000)
 
+        # create the encryption handler
+        encryptor = crypto.Encrypt (password="key",
+                                    version=DELTATAR_HEADER_VERSION,
+                                    paramversion=DELTATAR_PARAMETER_VERSION)
+
         # create the tar file with volumes
-        tarobj = TarFile.open("sample.tar.gz.aes128",
-                              mode="w#gz.aes128",
+        tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
+                              mode="w#gz",
                               format=GNU_FORMAT,
                               concat_compression=True,
-                              password='key')
+                              encryption=encryptor)
         tarobj.add("big")
         tarobj.close()
         os.unlink("big")
 
-        # extract with normal tar and check output
-        filesplit.split_file(b'Salted__', "sample.tar.gz.aes.", "sample.tar.gz.aes128")
+        #filesplit.split_file(b'Salted__', "sample.tar.gz.aes.", "sample.tar.gz.aes128")
 
-        assert os.path.exists("sample.tar.gz.aes.0") # beginning of the tar file
-        assert os.path.exists("sample.tar.gz.aes.1") # first file
+        # decrypt outer archive layer with crypto.py
+        assert os.path.exists("sample.tar.gz.pdtcrypt")
+        ret = os.system("python3 -s ./deltatar/crypto.py key <sample.tar.gz.pdtcrypt >sample.tar.gz")
+        assert ret == 0
+        assert os.path.exists("sample.tar.gz")
 
-        os.system("openssl aes-128-cbc -nopad -k 'key' -d -in sample.tar.gz.aes.1 -out sample.tar.gz")
+        # extract with normal tar and check output
         os.system("zcat sample.tar.gz 2>/dev/null > sample.tar")
         os.system("tar xf sample.tar")
         assert os.path.exists("big")
         assert hash == self.md5sum("big")
 
-    def test_openssl_multiple_files_decrypt(self):
+    def test_cli_multiple_files_decrypt(self):
         """
         Create a tar file with multiple files inside, using concat
-        compression and encryption mode. Then decrypt with openssl,
+        compression and encryption mode. Then decrypt with ``crypto.py``,
         decompress it with zcat and untar it with gnu tar.
         """
 
@@ -74,12 +95,17 @@ class EncryptionTest(BaseTest):
         hash["small"] = self.create_file("small", 100)
         hash["small2"] = self.create_file("small2", 354)
 
+        # create the encryption handler
+        encryptor = crypto.Encrypt (password="key",
+                                    version=DELTATAR_HEADER_VERSION,
+                                    paramversion=DELTATAR_PARAMETER_VERSION)
+
         # create the tar file with volumes
-        tarobj = TarFile.open("sample.tar.gz.aes128",
-                              mode="w#gz.aes128",
+        tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
+                              mode="w#gz",
                               format=GNU_FORMAT,
                               concat_compression=True,
-                              password='key')
+                              encryption=encryptor)
 
         for k in hash:
             tarobj.add(k)
@@ -88,26 +114,18 @@ class EncryptionTest(BaseTest):
         for k in hash:
             os.unlink(k)
 
-        # extract with normal tar and check output
-        filesplit.split_file(b'Salted__', "sample.tar.gz.aes.", "sample.tar.gz.aes128")
-
-        assert os.path.exists("sample.tar.gz.aes.0") # beginning of the tar file
-        assert os.path.exists("sample.tar.gz.aes.1") # first file
-        assert os.path.exists("sample.tar.gz.aes.2") # second file
-        assert os.path.exists("sample.tar.gz.aes.3") # third file
-        assert not os.path.exists("sample.tar.gz.aes.4") # nothing else
-
-        # extract and check output
-        for i in range(1, 4):
-            fname = "sample.tar.gz.aes.%d" % i
-            os.system("openssl aes-128-cbc -nopad -k 'key' -d -in %s -out sample.tar.gz" % fname)
-            os.system("zcat sample.tar.gz 2>/dev/null > sample.tar")
-            os.system("tar xf sample.tar")
+        assert os.path.exists("sample.tar.gz.pdtcrypt")
+        ret = os.system("python3 -s ./deltatar/crypto.py key <sample.tar.gz.pdtcrypt >sample.tar.gz")
+        assert ret == 0
+        assert os.path.exists("sample.tar.gz")
+        os.system("zcat sample.tar.gz 2>/dev/null > sample.tar")
+        os.system("tar xf sample.tar")
 
         for key, value in hash.items():
             assert os.path.exists(key)
             assert value == self.md5sum(key)
 
+
     def test_decrypt(self):
         """
         Create a tar file with only one file inside, using concat
@@ -117,20 +135,26 @@ class EncryptionTest(BaseTest):
         # create the content of the file to compress and hash it
         hash = self.create_file("big", 50000)
 
+        # encryption handling
+        encryptor = crypto.Encrypt (password="key",
+                                    version=DELTATAR_HEADER_VERSION,
+                                    paramversion=DELTATAR_PARAMETER_VERSION)
+        decryptor = crypto.Decrypt (password="key")
+
         # create the tar file with volumes
-        tarobj = TarFile.open("sample.tar.gz.aes128",
-                              mode="w#gz.aes128",
+        tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
+                              mode="w#gz",
                               format=GNU_FORMAT,
                               concat_compression=True,
-                              password='key')
+                              encryption=encryptor)
         tarobj.add("big")
         tarobj.close()
         os.unlink("big")
 
-        tarobj = TarFile.open("sample.tar.gz.aes128",
-                              mode="r#gz.aes128",
+        tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
+                              mode="r#gz",
                               format=GNU_FORMAT,
-                              password='key')
+                              encryption = decryptor)
         tarobj.extractall()
         tarobj.close()
         assert os.path.exists("big")
@@ -149,12 +173,16 @@ class EncryptionTest(BaseTest):
         hash["small"] = self.create_file("small", 100)
         hash["small2"] = self.create_file("small2", 354)
 
+        encryptor = crypto.Encrypt (password="key",
+                                    version=DELTATAR_HEADER_VERSION,
+                                    paramversion=DELTATAR_PARAMETER_VERSION)
+
         # create the tar file with volumes
-        tarobj = TarFile.open("sample.tar.gz.aes128",
-                              mode="w#gz.aes128",
+        tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
+                              mode="w#gz",
                               format=GNU_FORMAT,
                               concat_compression=True,
-                              password='key')
+                              encryption=encryptor)
 
         for k in hash:
             tarobj.add(k)
@@ -163,10 +191,12 @@ class EncryptionTest(BaseTest):
         for k in hash:
             os.unlink(k)
 
-        tarobj = TarFile.open("sample.tar.gz.aes128",
-                              mode="r#gz.aes128",
+        decryptor = crypto.Decrypt (password="key")
+        tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
+                              mode="r#gz",
                               format=GNU_FORMAT,
-                              password='key')
+                              encryption=decryptor)
+
         tarobj.extractall()
         tarobj.close()
 
@@ -174,6 +204,7 @@ class EncryptionTest(BaseTest):
             assert os.path.exists(key)
             assert value == self.md5sum(key)
 
+
     def test_multivol_file_decrypt(self):
         '''
         Test multivol tarball with encryption.
@@ -186,68 +217,44 @@ class EncryptionTest(BaseTest):
         hash["small"] = self.create_file("small", 100)
         hash["small2"] = self.create_file("small2", 354)
 
-        # create the tar file with volumes
-        tarobj = TarFile.open("sample.tar.gz.aes128",
-                              mode="w#gz.aes128",
-                              password='key',
-                              concat_compression=True,
-                              max_volume_size=20000,
-                              new_volume_handler=new_volume_handler)
-
-        for k in hash:
-            tarobj.add(k)
-        tarobj.close()
-
-        assert os.path.exists("sample.tar.gz.aes128")
-        for k in hash:
-            os.unlink(k)
-
-        # extract
-        tarobj = TarFile.open("sample.tar.gz.aes128",
-                              mode="r#gz.aes128",
-                              password="key",
-                              new_volume_handler=new_volume_handler)
-        tarobj.extractall()
-        tarobj.close()
-
-        # check output
-        for key, value in hash.items():
-            assert os.path.exists(key)
-            assert value == self.md5sum(key)
-
-    def test_multivol_file_decrypt_aes256(self):
-        '''
-        Test multivol tarball with encryption aes256
-        '''
+        # encryption handler
+        encryptor = crypto.Encrypt (password="key",
+                                    version=DELTATAR_HEADER_VERSION,
+                                    paramversion=DELTATAR_PARAMETER_VERSION)
 
-        # create sample data
-        hash = dict()
-        hash["big"] = self.create_file("big", 50000)
-        hash["big2"] = self.create_file("big2", 10200)
-        hash["small"] = self.create_file("small", 100)
-        hash["small2"] = self.create_file("small2", 354)
+        # plug the encryption context into the volume handler
+        encrypt_volume = make_new_encryption_volume_handler (encryptor)
 
-        # create the tar file with volumes
-        tarobj = TarFile.open("sample.tar.gz.aes256",
-                              mode="w#gz.aes256",
-                              password='key',
+        # create the tar file with volumes; we need to use a low compression
+        # level to force volume split because the test data has too little
+        # entropy
+        tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
+                              mode="w#gz",
                               concat_compression=True,
                               max_volume_size=20000,
-                              new_volume_handler=new_volume_handler)
+                              compresslevel=0,
+                              new_volume_handler=encrypt_volume,
+                              encryption=encryptor)
 
         for k in hash:
             tarobj.add(k)
         tarobj.close()
 
-        assert os.path.exists("sample.tar.gz.aes256")
+        assert os.path.exists("sample.tar.gz.pdtcrypt")
         for k in hash:
             os.unlink(k)
 
+        # decrypt
+        decryptor = crypto.Decrypt (password="key")
+
+        # plug the encryption context into the volume handler
+        decrypt_volume = make_new_encryption_volume_handler (decryptor)
+
         # extract
-        tarobj = TarFile.open("sample.tar.gz.aes256",
-                              mode="r#gz.aes256",
-                              password="key",
-                              new_volume_handler=new_volume_handler)
+        tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
+                              mode="r#gz",
+                              new_volume_handler=decrypt_volume,
+                              encryption=decryptor)
         tarobj.extractall()
         tarobj.close()
 
@@ -256,36 +263,3 @@ class EncryptionTest(BaseTest):
             assert os.path.exists(key)
             assert value == self.md5sum(key)
 
-    def test_openssl_decrypt_256(self):
-        """
-        Create a tar file with only one file inside, using concat
-        compression and encryption mode. Then decrypt with openssl,
-        decompress it with zcat and untar it with gnu tar.
-
-        Using aes 256.
-        """
-
-        # create the content of the file to compress and hash it
-        hash = self.create_file("big", 50000)
-
-        # create the tar file with volumes
-        tarobj = TarFile.open("sample.tar.gz.aes256",
-                              mode="w#gz.aes256",
-                              format=GNU_FORMAT,
-                              concat_compression=True,
-                              password='key')
-        tarobj.add("big")
-        tarobj.close()
-        os.unlink("big")
-
-        # extract with normal tar and check output
-        filesplit.split_file(b'Salted__', "sample.tar.gz.aes.", "sample.tar.gz.aes256")
-
-        assert os.path.exists("sample.tar.gz.aes.0") # beginning of the tar file
-        assert os.path.exists("sample.tar.gz.aes.1") # first file
-
-        os.system("openssl aes-256-cbc -nopad -k 'key' -d -in sample.tar.gz.aes.1 -out sample.tar.gz")
-        os.system("zcat sample.tar.gz 2>/dev/null > sample.tar")
-        os.system("tar xf sample.tar")
-        assert os.path.exists("big")
-        assert hash == self.md5sum("big")