from functools import partial
from . import tarfile
+from . import crypto
class NullHandler(logging.Handler):
def emit(self, record):
# used together with aes modes to encrypt and decrypt backups.
password = None
+ # when encrypting or decrypting, this holds crypto handler; created before
+ # establishing the Tarfile stream iff a password is supplied.
+ crypto_ctx = None
+
# When encrypting, the salt is created by the first crypto operation, i. e.
# opening the index for writing. The subsequently opened archive uses the
# same salt.
# specifies the index mode in the same format as @param mode, but without
# the ':', '|' or '#' at the begining. It doesn't make sense to specify
- # that the index is encrypted if no no password is given in the constructor.
+ # that the index is encrypted if no password is given in the constructor.
index_mode = None
# current time for this backup. Used for file names and file creation checks
if logger:
self.logger.addHandler(logger)
self.mode = mode
- self.password = password
+
+ if password is not None:
+ self.password = password
# generate index_mode
if index_mode is None:
layer.
:type kind: str
'''
- filemode = None
-
if self.index_mode.startswith('gz'):
comptype = 'gz'
elif self.index_mode.startswith('bz2'):
encver = None
counter = None
- if 'aes' in self.index_mode:
- encver = I2N_XXX_ENCRYPTION_VERSION
- counter = None
- if kind == "info": # fixed counter
- counter = crypto.AES_GCM_IV_CNT_INFOFILE
+ if self.crypto_ctx is not None and kind == "info":
+ counter = crypto.AES_GCM_IV_CNT_INFOFILE
return tarfile._Stream(name=path, mode=mode, comptype=comptype,
bufsize=tarfile.RECORDSIZE, fileobj=None,
- encver=encver, enccounter=counter,
- password=self.password)
+ encryption=self.crypto_ctx, enccounter=counter)
def create_full_backup(self, source_path, backup_path,
max_volume_size=None, extra_data=dict()):
if self.mode not in self.__file_extensions_dict:
raise Exception('Unrecognized extension')
+ # setup for encrypting payload
+ if self.password is not None:
+ self.crypto_ctx = crypto.Encrypt (self.password,
+ paramversion=I2N_XXX_ENCRYPTION_VERSION)
+
# some initialization
self.vol_no = 0
mode='w' + self.mode,
format=tarfile.GNU_FORMAT,
concat_compression='#gz' in self.mode,
- password=self.password,
- nacl=self.nacl,
+ encryption=self.crypto_ctx,
max_volume_size=max_volume_size,
new_volume_handler=new_volume_handler,
save_to_members=False,
if self.mode not in self.__file_extensions_dict:
raise Exception('Unrecognized extension')
+ # setup for encrypting payload
+ if self.password is not None:
+ self.crypto_ctx = crypto.Encrypt (self.password,
+ paramversion=I2N_XXX_ENCRYPTION_VERSION)
# some initialization
self.vol_no = 0
cwd = os.getcwd()
os.chdir(target_path)
+ # setup for decrypting payload
+ if self.password is not None:
+ self.crypto_ctx = crypto.Decrypt (self.password)
+
if mode == 'tar':
index_it = self.iterate_tar_path(backup_tar_path)
helper = RestoreHelper(self, cwd, backup_path=backup_tar_path,
def __init__(self, name, mode, comptype, fileobj, bufsize,
concat_stream=False,
- encver=None, enccounter=None, password=None,
- nacl=None, compresslevel=9):
+ encryption=None, enccounter=None,
+ compresslevel=9):
"""Construct a _Stream object.
"""
self._extfileobj = True
self.compresslevel = compresslevel
self.bytes_written = 0
# crypto parameters
- self.encver = encver
- self.password = password
- self.encryption = None
+ self.encryption = encryption
self.lasthdr = None
enccounter = enccounter or crypto.AES_GCM_IV_CNT_DATA
self.zlib = zlib
if mode == "r":
self._init_read_gz()
- if self.encver is not None:
- if password is None:
- raise InvalidEncryptionError \
- ("encryption requested (v=%d) but no password given"
- % encver)
- try:
- enc = crypto.Decrypt (password)
- except ValueError as exn:
- raise InvalidEncryptionError \
- ("ctor failed crypto.Decrypt(<PASSWORD>)")
- self.encryption = enc
- else:
- if self.encver is not None:
- # Layers are stacked differently: initialization is
- # necessary per file.
- if password is None:
- raise InvalidEncryptionError \
- ("encryption requested (v=%d) but no password given"
- % encver)
- try:
- enc = crypto.Encrypt (password,
- I2N_XXX_ENCRYPTION_VERSION, nacl,
- counter=enccounter)
- except ValueError as exn:
- raise InvalidEncryptionError \
- ("ctor failed crypto.Encrypt(<PASSWORD>, “%s”, %r)"
- % (nacl, 1))
- self.encryption = enc
self.exception = zlib.error # XXX what for? seems unused
self.crc = zlib.crc32(b"") & 0xFFFFffff
elif comptype == "bz2":
- if self.encver is not None:
- raise InvalidEncryptionError("encryption version %r not "
- "available for compression %s"
- % (encver, comptype))
+ if self.encryption is not None:
+ raise InvalidEncryptionError("encryption not available for "
+ "compression %s" % comptype)
try:
import bz2
except ImportError:
self.cmp = bz2.BZ2Compressor()
elif comptype == 'xz':
- if self.encver is not None:
- raise InvalidEncryptionError("encryption version %r not "
- "available for compression %s"
- % (encver, comptype))
+ if self.encryption is not None:
+ raise InvalidEncryptionError("encryption not available for "
+ "compression %s" % comptype)
try:
import lzma
except ImportError:
self.cmp = lzma.LZMACompressor()
elif comptype != "tar":
- if self.encver is not None:
- raise InvalidEncryptionError("encryption version %r not "
- "available for compression %s"
- % (encver, comptype))
+ if self.encryption is not None:
+ raise InvalidEncryptionError("encryption not available for "
+ "compression %s" % comptype)
raise CompressionError("unknown compression type %r" % comptype)
- else: # no compression
- if mode == "r":
- if password is None:
- raise InvalidEncryptionError \
- ("encryption requested (v=%d) but no password given"
- % encver)
- try:
- enc = crypto.Decrypt (password)
- except ValueError as exn:
- raise InvalidEncryptionError \
- ("ctor failed crypto.Decrypt(<PASSWORD>)")
- self.encryption = enc
- elif mode == "w":
- if password is None:
- raise InvalidEncryptionError \
- ("encryption requested (v=%d) but no password given"
- % encver)
- try:
- enc = crypto.Encrypt (password,
- I2N_XXX_ENCRYPTION_VERSION, nacl,
- counter=enccounter)
- except ValueError as exn:
- raise InvalidEncryptionError \
- ("ctor failed crypto.Encrypt(<PASSWORD>, “%s”, %r)"
- % (nacl, 1))
- self.encryption = enc
-
except: # XXX seriously?
if not self._extfileobj:
self.fileobj.close()
@classmethod
def open(cls, name=None, mode="r", fileobj=None, bufsize=RECORDSIZE,
- compresslevel=9, **kwargs):
+ encryption=None, compresslevel=9, **kwargs):
"""Open a tar archive for reading, writing or appending. Return
an appropriate TarFile class.
filemode, comptype = mode.split("#", 1)
filemode = filemode or "r"
password = ''
- nacl = None
- encver = None
if filemode not in "rw":
raise ValueError("mode must be 'r' or 'w'")
- # XXX add equivalent check for defined modes
- #if comptype not in ["gz", "gz.aes128", "gz.aes256", 'aes128',
- # 'aes256']:
- # raise ValueError("comptype must be 'gz' or 'aes'")
-
- # encryption gz.aes128 or gz.aes256
- if "." in comptype:
- comptype, _ = comptype.split(".", 1)
- encver = I2N_XXX_ENCRYPTION_VERSION # XXX set dynamically
- password = kwargs.get('password', '')
- if not password:
- raise ValueError("you should give a password for encryption")
-
- if comptype.startswith("aes"):
- comptype = 'tar'
- encver = I2N_XXX_ENCRYPTION_VERSION # XXX set dynamically
- password = kwargs.get ("password")
- if password is None:
- raise ValueError("you should give a password for encryption")
-
kwargs['concat_compression'] = True
stream = _Stream(name, filemode, comptype, fileobj, bufsize,
- concat_stream=True, encver=encver,
- password=password, nacl=nacl,
+ concat_stream=True, encryption=encryption,
compresslevel=compresslevel)
try:
t = cls(name, filemode, stream, **kwargs)