From: Philipp Gesang Date: Tue, 25 Apr 2017 09:13:17 +0000 (+0200) Subject: keep separate encryptor and decryptor contexts in deltatar.py X-Git-Tag: v2.2~7^2~147 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=133d30da69daa160eb26bbfb4f3630da4490b266;p=python-delta-tar keep separate encryptor and decryptor contexts in deltatar.py The same Deltatar object appears to function as handle for reading and writing files simultaneously. To support this, introduce two different crypto contexts that are created on demand. --- diff --git a/deltatar/deltatar.py b/deltatar/deltatar.py index 6f21340..d28617a 100644 --- a/deltatar/deltatar.py +++ b/deltatar/deltatar.py @@ -50,6 +50,10 @@ NO_MATCH = False MATCH = True PARENT_MATCH = 2 +# encryption direction +CRYPTO_MODE_ENCRYPT = 0 +CRYPTO_MODE_DECRYPT = 1 + # The canonical extension for encrypted backup files regardless of the actual # encryption parameters is “.pdtcrypt”. This is analogous to the encryption # header which starts with the eight ASCII bytes “PDTCRYPT”. Historical note: @@ -94,9 +98,10 @@ class DeltaTar(object): # on decryption since the required settings are determined from the headers crypto_paramversion = None - # when encrypting or decrypting, this holds crypto handler; created before + # when encrypting or decrypting, these hold crypto handlers; created before # establishing the Tarfile stream iff a password is supplied. - crypto_ctx = None + encryptor = None + decryptor = None # python logger object. logger = None @@ -257,7 +262,7 @@ class DeltaTar(object): if kind == PDT_TYPE_ARCHIVE: ret += ".tar" ret += mode - if self.crypto_ctx is not None: + if self.encryptor is not None or self.decryptor is not None: ret += "." + PDTCRYPT_EXTENSION return ret @@ -511,6 +516,27 @@ class DeltaTar(object): return path[len(prefix):] return path + + def initialize_encryption (self, mode): + password = self.password + + if password is None: + return + if mode == CRYPTO_MODE_ENCRYPT: + if self.encryptor is None: + self.encryptor = \ + crypto.Encrypt (password, + version=DELTATAR_HEADER_VERSION, + paramversion=self.crypto_paramversion) + return + if mode == CRYPTO_MODE_DECRYPT: + if self.decryptor is None: + self.decryptor = crypto.Decrypt (password) + return + + raise Exception ("invalid encryption mode [%r]" % mode) + + def open_auxiliary_file(self, path, mode='r', kind=AUXILIARY_FILE_INDEX): ''' Given the specified configuration, opens a file for reading or writing, @@ -532,10 +558,18 @@ class DeltaTar(object): else: comptype = 'tar' + crypto_ctx = None + if mode == "w": + self.initialize_encryption (CRYPTO_MODE_ENCRYPT) + crypto_ctx = self.encryptor + elif mode == "r": + self.initialize_encryption (CRYPTO_MODE_DECRYPT) + crypto_ctx = self.decryptor + sink = tarfile._Stream(name=path, mode=mode, comptype=comptype, bufsize=tarfile.RECORDSIZE, fileobj=None, - encryption=self.crypto_ctx) - if self.crypto_ctx is not None and mode == "w": + encryption=crypto_ctx) + if self.encryptor is not None and mode == "w": counter = None if kind == AUXILIARY_FILE_INFO: counter = crypto.AES_GCM_IV_CNT_INFOFILE @@ -606,11 +640,7 @@ class DeltaTar(object): raise Exception('Unrecognized extension') # setup for encrypting payload - if self.password is not None: - self.crypto_ctx = \ - crypto.Encrypt (self.password, - version=DELTATAR_HEADER_VERSION, - paramversion=self.crypto_paramversion) + self.initialize_encryption (CRYPTO_MODE_ENCRYPT) # some initialization self.vol_no = 0 @@ -643,7 +673,7 @@ class DeltaTar(object): tarobj.open_volume(volume_path, encryption=encryption) # wraps some args from context into the handler - new_volume_handler = partial(new_volume_handler, self, cwd, backup_path, self.crypto_ctx) + new_volume_handler = partial(new_volume_handler, self, cwd, backup_path, self.encryptor) index_accu.write(bytes('{"type": "python-delta-tar-index", "version": 1, "backup-type": "full", "extra_data": %s}\n' % extra_data_str, 'UTF-8')) @@ -657,7 +687,7 @@ class DeltaTar(object): mode='w' + self.mode, format=tarfile.GNU_FORMAT, concat_compression='#gz' in self.mode, - encryption=self.crypto_ctx, + encryption=self.encryptor, max_volume_size=max_volume_size, new_volume_handler=new_volume_handler, save_to_members=False, @@ -773,11 +803,8 @@ class DeltaTar(object): raise Exception('Unrecognized extension') # setup for encrypting payload - if self.password is not None: - self.crypto_ctx = \ - crypto.Encrypt (self.password, - version=DELTATAR_HEADER_VERSION, - paramversion=self.crypto_paramversion) + self.initialize_encryption (CRYPTO_MODE_ENCRYPT) + # some initialization self.vol_no = 0 @@ -823,7 +850,7 @@ class DeltaTar(object): mode='w' + self.mode, format=tarfile.GNU_FORMAT, concat_compression='#gz' in self.mode, - encryption=self.crypto_ctx, + encryption=self.encryptor, max_volume_size=max_volume_size, new_volume_handler=new_volume_handler, save_to_members=False, @@ -1042,7 +1069,7 @@ class DeltaTar(object): mode='r' + self.delta_tar.mode, format=tarfile.GNU_FORMAT, concat_compression='#gz' in self.delta_tar.mode, - encryption=self.delta_tar.crypto_ctx, + encryption=self.delta_tar.decryptor, new_volume_handler=self.new_volume_handler, save_to_members=False, dereference=True) @@ -1209,19 +1236,18 @@ class DeltaTar(object): volume_path = os.path.join(cwd, volume_path) tarobj.open_volume(volume_path, encryption=encryption) - if self.password is not None: - self.crypto_ctx = crypto.Decrypt (self.password) + self.initialize_encryption (CRYPTO_MODE_DECRYPT) backup_path = os.path.dirname(backup_tar_path) if not os.path.isabs(backup_path): backup_path = os.path.join(cwd, backup_path) - new_volume_handler = partial(new_volume_handler, self, cwd, backup_path, self.crypto_ctx) + new_volume_handler = partial(new_volume_handler, self, cwd, backup_path, self.decryptor) tarobj = tarfile.TarFile.open(backup_tar_path, mode='r' + self.mode, format=tarfile.GNU_FORMAT, concat_compression='#gz' in self.mode, - encryption=self.crypto_ctx, + encryption=self.decryptor, new_volume_handler=new_volume_handler, save_to_members=False, dereference=True) @@ -1318,7 +1344,7 @@ class DeltaTar(object): # setup for decrypting payload if self.password is not None: - self.crypto_ctx = crypto.Decrypt (self.password) + self.decryptor = crypto.Decrypt (self.password) if mode == 'tar': index_it = self.iterate_tar_path(backup_tar_path) @@ -1464,7 +1490,7 @@ class RestoreHelper(object): last_lno = 0, new_volume_handler = partial(self.new_volume_handler, self._deltatar, self._cwd, is_full, - os.path.dirname(index), self._deltatar.crypto_ctx) + os.path.dirname(index), self._deltatar.decryptor) ) self._data.append(s) else: @@ -1475,7 +1501,7 @@ class RestoreHelper(object): # update the new_volume_handler of tar_obj tarobj.new_volume_handler = partial(self.new_volume_handler, self._deltatar, self._cwd, True, os.path.dirname(backup_path), - self._deltatar.crypto_ctx) + self._deltatar.decryptor) s = dict( curr_vol_no = None, vol_fd = None, @@ -1730,7 +1756,7 @@ class RestoreHelper(object): fileobj=index_data['vol_fd'], format=tarfile.GNU_FORMAT, concat_compression='#gz' in self._deltatar.mode, - encryption=self._deltatar.crypto_ctx, + encryption=self._deltatar.decryptor, new_volume_handler=index_data['new_volume_handler'], save_to_members=False) diff --git a/deltatar/tarfile.py b/deltatar/tarfile.py index 508f5a3..e3659a3 100644 --- a/deltatar/tarfile.py +++ b/deltatar/tarfile.py @@ -463,7 +463,7 @@ class _Stream: self.zlib = zlib if mode == "r": self.exception = zlib.error - if concat_stream is True: + if self.encryption is None: self._init_read_gz() elif mode == "w": if self.encryption is None and concat_stream is False: @@ -2037,7 +2037,7 @@ class TarFile(object): elif mode in "aw": return cls.taropen(name, mode, fileobj, **kwargs) - raise ValueError("undiscernible mode") + raise ValueError("undiscernible mode %r" % mode) @classmethod def taropen(cls, name, mode="r", fileobj=None, **kwargs):