1 # Copyright (C) 2013 Intra2net AG
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU Lesser General Public License as published
5 # by the Free Software Foundation; either version 3 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU Lesser General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see
15 # <http://www.gnu.org/licenses/lgpl-3.0.html>
24 from deltatar import crypto
25 from deltatar.tarfile import TarFile, GNU_FORMAT
28 from . import BaseTest
29 from . import new_volume_handler, make_new_encryption_volume_handler
31 DELTATAR_HEADER_VERSION = 1
32 DELTATAR_PARAMETER_VERSION = 1
34 class EncryptionTest(BaseTest):
36 Test encryption after compression in tarfiles
39 def test_cli_decrypt_pw (self):
41 Create a tar file with only one file inside, using concat
42 compression and encryption mode. Then decrypt with crypto.py,
43 decompress it with zcat and untar it with gnu tar.
45 Note that in an earlier implementation of the Deltatar crypto
46 layer, files could be decrypted directly using OpenSSL command
47 line tools. With the version 1 parameters this is no longer
48 possible since OpenSSL does not ship with a command line tool
49 that understands GCM and even if it did, it would be very
50 unlikely that it could be made with the raw pdtcrypt format.
52 Thus, we rely on the functionality of our encryption library
53 ``crypto.py`` to decrypt on the command line; the rest of the
54 data pipeline (→ gzip → tar → files) remains the same.
56 pw = "Where is my cow?"
58 # create the content of the file to compress and hash it
59 hash = self.create_file("big", 50000)
61 # create the encryption handler
62 encryptor = crypto.Encrypt (password=pw,
63 version=DELTATAR_HEADER_VERSION,
64 paramversion=DELTATAR_PARAMETER_VERSION)
66 # create the tar file with volumes
67 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
75 # decrypt outer archive layer with crypto.py
76 assert os.path.exists("sample.tar.gz.pdtcrypt")
77 ret = os.system("python3 ./deltatar/crypto.py process -p '%s' "
78 "<sample.tar.gz.pdtcrypt >sample.tar.gz" % pw)
80 assert os.path.exists("sample.tar.gz")
82 # extract with normal tar and check output
83 os.system("zcat sample.tar.gz 2>/dev/null > sample.tar")
84 os.system("tar xf sample.tar </dev/null")
85 assert os.path.exists("big")
86 assert hash == self.md5sum("big")
89 def test_cli_decrypt_key_argv (self):
91 Encrypt, then decrypt using the crypto.py → gzip → tar pipe,
92 supplying the encryption key on the command line.
95 nacl = hashlib.md5 ("Stráðu á mig salti".encode ()).digest ()
97 # create the content of the file to compress and hash it
98 hash = self.create_file("big", 50000)
100 # create the encryption handler
101 encryptor = crypto.Encrypt (key=key, nacl=nacl,
102 version=DELTATAR_HEADER_VERSION,
103 paramversion=DELTATAR_PARAMETER_VERSION)
105 # create the tar file with volumes
106 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
109 encryption=encryptor)
114 # decrypt outer archive layer with crypto.py
115 assert os.path.exists("sample.tar.gz.pdtcrypt")
116 ret = os.system("python3 ./deltatar/crypto.py process -k '%s' "
117 "<sample.tar.gz.pdtcrypt >sample.tar.gz"
118 % binascii.hexlify (key).decode ())
120 assert os.path.exists("sample.tar.gz")
122 # extract with normal tar and check output
123 os.system("zcat sample.tar.gz 2>/dev/null > sample.tar")
124 os.system("tar xf sample.tar </dev/null")
125 assert os.path.exists("big")
126 assert hash == self.md5sum("big")
129 def test_cli_decrypt_key_envp (self):
131 Encrypt, then decrypt using the crypto.py → gzip → tar pipe,
132 supplying the encryption key through the process environment.
134 key = os.urandom (16)
135 nacl = hashlib.md5 ("Stráðu á mig salti".encode ()).digest ()
137 # create the content of the file to compress and hash it
138 hash = self.create_file("big", 50000)
140 # create the encryption handler
141 encryptor = crypto.Encrypt (key=key, nacl=nacl,
142 version=DELTATAR_HEADER_VERSION,
143 paramversion=DELTATAR_PARAMETER_VERSION)
145 # create the tar file with volumes
146 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
149 encryption=encryptor)
154 # decrypt outer archive layer with crypto.py
155 assert os.path.exists("sample.tar.gz.pdtcrypt")
156 ret = os.system("PDTCRYPT_KEY='%s' python3 ./deltatar/crypto.py process "
157 "<sample.tar.gz.pdtcrypt >sample.tar.gz"
158 % binascii.hexlify (key).decode ())
160 assert os.path.exists("sample.tar.gz")
162 # extract with normal tar and check output
163 os.system("zcat sample.tar.gz 2>/dev/null > sample.tar")
164 os.system("tar xf sample.tar </dev/null")
165 assert os.path.exists("big")
166 assert hash == self.md5sum("big")
169 def test_cli_scrypt (self):
171 Create an encrypted archive, then have crypto.py extract the
174 pw = "It goes baah, it is a sheep."
175 nacl = hashlib.md5 ("Stráðu á mig salti".encode ()).digest ()
177 # create the content of the file to compress and hash it
178 _void = self.create_file("big", 50000)
180 # create the encryption handler
181 encryptor = crypto.Encrypt (password=pw, nacl=nacl,
182 version=DELTATAR_HEADER_VERSION,
183 paramversion=DELTATAR_PARAMETER_VERSION)
185 # create the tar file with volumes
186 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
189 encryption=encryptor)
194 # decrypt outer archive layer with crypto.py
195 assert os.path.exists("sample.tar.gz.pdtcrypt")
196 with subprocess.Popen ( [ "python3", "./deltatar/crypto.py", "scrypt"
198 , "-i", "sample.tar.gz.pdtcrypt" ]
199 , env={ "PDTCRYPT_PASSWORD" : pw }
200 , stdout=subprocess.PIPE
202 raw = p.stdout.read ().strip ().decode ()
204 info = json.loads (raw)
205 assert nacl == binascii.unhexlify (info ["salt"])
206 key = binascii.unhexlify (info ["key"])
207 kdf = crypto.kdf_by_version (1)
208 assert key, nacl == kdf (pw.encode (), nacl)
211 def test_cli_multiple_files_decrypt_pw_argv (self):
213 Create a tar file with multiple files inside, using concat
214 compression and encryption mode. Then decrypt and split with
215 ``crypto.py``, decompress it with zcat and untar it with gnu tar.
216 The password is specified as command line argument.
218 pw = "Is that my cow?"
222 hash["big"] = self.create_file("big", 50000)
223 hash["small"] = self.create_file("small", 100)
224 hash["small2"] = self.create_file("small2", 354)
226 # create the encryption handler
227 encryptor = crypto.Encrypt (password=pw,
228 version=DELTATAR_HEADER_VERSION,
229 paramversion=DELTATAR_PARAMETER_VERSION)
231 # create the tar file with volumes
232 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
235 encryption=encryptor)
244 assert os.path.exists("sample.tar.gz.pdtcrypt")
245 ret = os.system("python3 ./deltatar/crypto.py process -p '%s' -s --split "
246 "-i sample.tar.gz.pdtcrypt -o ." % pw)
248 for i in range (len (hash)):
249 fname = "pdtcrypt-object-%d.bin" % (i + 1)
250 assert os.path.exists(fname)
251 os.system("zcat '%s' 2>/dev/null > sample.tar" % fname)
252 os.system("tar xf sample.tar </dev/null")
254 for fname, digest in hash.items():
255 assert os.path.exists(fname)
256 assert digest == self.md5sum(fname)
259 def test_cli_multiple_files_decrypt_envp (self):
261 Create a tar file with multiple files inside, using concat
262 compression and encryption mode. Then decrypt and split with
263 ``crypto.py``, decompress it with zcat and untar it with gnu tar.
264 The password is given as environment variable.
266 pw = "Is that my cow?"
270 hash["big"] = self.create_file("big", 50000)
271 hash["small"] = self.create_file("small", 100)
272 hash["small2"] = self.create_file("small2", 354)
274 # create the encryption handler
275 encryptor = crypto.Encrypt (password=pw,
276 version=DELTATAR_HEADER_VERSION,
277 paramversion=DELTATAR_PARAMETER_VERSION)
279 # create the tar file with volumes
280 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
283 encryption=encryptor)
292 assert os.path.exists("sample.tar.gz.pdtcrypt")
293 ret = os.system("PDTCRYPT_PASSWORD='%s' python3 ./deltatar/crypto.py "
294 "process -s --split -i sample.tar.gz.pdtcrypt -o ."
297 for i in range (len (hash)):
298 fname = "pdtcrypt-object-%d.bin" % (i + 1)
299 assert os.path.exists(fname)
300 os.system("zcat '%s' 2>/dev/null > sample.tar" % fname)
301 os.system("tar xf sample.tar </dev/null")
303 for fname, digest in hash.items():
304 assert os.path.exists(fname)
305 assert digest == self.md5sum(fname)
308 def test_decrypt(self):
310 Create a tar file with only one file inside, using concat
311 compression and encryption mode. Then decrypt it.
314 # create the content of the file to compress and hash it
315 hash = self.create_file("big", 50000)
317 # encryption handling
318 encryptor = crypto.Encrypt (password="key",
319 version=DELTATAR_HEADER_VERSION,
320 paramversion=DELTATAR_PARAMETER_VERSION)
321 decryptor = crypto.Decrypt (password="key")
323 # create the tar file with volumes
324 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
327 encryption=encryptor)
332 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
335 encryption = decryptor)
338 assert os.path.exists("big")
339 assert hash == self.md5sum("big")
342 def test_multiple_file_decrypt(self):
344 Create a tar file with only one file inside, using concat
345 compression and encryption mode. Then decrypt it.
350 hash["big"] = self.create_file("big", 50000)
351 hash["small"] = self.create_file("small", 100)
352 hash["small2"] = self.create_file("small2", 354)
354 encryptor = crypto.Encrypt (password="key",
355 version=DELTATAR_HEADER_VERSION,
356 paramversion=DELTATAR_PARAMETER_VERSION)
358 # create the tar file with volumes
359 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
362 encryption=encryptor)
371 decryptor = crypto.Decrypt (password="key")
372 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
375 encryption=decryptor)
380 for key, value in hash.items():
381 assert os.path.exists(key)
382 assert value == self.md5sum(key)
385 def test_multivol_file_decrypt(self):
387 Test multivol tarball with encryption.
392 hash["big"] = self.create_file("big", 50000)
393 hash["big2"] = self.create_file("big2", 10200)
394 hash["small"] = self.create_file("small", 100)
395 hash["small2"] = self.create_file("small2", 354)
398 encryptor = crypto.Encrypt (password="key",
399 version=DELTATAR_HEADER_VERSION,
400 paramversion=DELTATAR_PARAMETER_VERSION)
402 # plug the encryption context into the volume handler
403 encrypt_volume = make_new_encryption_volume_handler (encryptor)
405 # create the tar file with volumes; we need to use a low compression
406 # level to force volume split because the test data has too little
408 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
410 max_volume_size=20000,
412 new_volume_handler=encrypt_volume,
413 encryption=encryptor)
419 assert os.path.exists("sample.tar.gz.pdtcrypt")
424 decryptor = crypto.Decrypt (password="key")
426 # plug the encryption context into the volume handler
427 decrypt_volume = make_new_encryption_volume_handler (decryptor)
430 tarobj = TarFile.open("sample.tar.gz.pdtcrypt",
432 new_volume_handler=decrypt_volume,
433 encryption=decryptor)
438 for key, value in hash.items():
439 assert os.path.exists(key)
440 assert value == self.md5sum(key)