From: Philipp Gesang Date: Mon, 10 Apr 2017 08:13:02 +0000 (+0200) Subject: unify error and parameter handling in crypto.py X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=fa28a9a360b9f45e0b78d717fc2244f0df8a384e;p=python-delta-tar unify error and parameter handling in crypto.py Three classes of errors: - bad encryption (tag mismatch, bad IVs); - bad user input (request info counter twice); - internal error (state was reached that indicates a problem with crypto.py). --- diff --git a/deltatar/crypto.py b/deltatar/crypto.py index 35bf87b..17aa074 100755 --- a/deltatar/crypto.py +++ b/deltatar/crypto.py @@ -67,6 +67,10 @@ class EndOfFile (Exception): """Reached EOF.""" pass +class InvalidParameter (Exception): + """Inputs not valid for PDT encryption.""" + pass + class InvalidHeader (Exception): """Header not valid.""" pass @@ -75,6 +79,10 @@ class InvalidIVFixedPart (Exception): """IV fixed part not in supplied list.""" pass +class FormatError (Exception): + """Unusable parameters in header.""" + pass + class DecryptionError (Exception): """Error during decryption.""" pass @@ -83,6 +91,10 @@ class Unreachable (Exception): """Makeshift __builtin_unreachable().""" pass +class InternalError (Exception): + """Errors not ascribable to bad user inputs or cryptography.""" + pass + ############################################################################### ## crypto layer version @@ -350,7 +362,7 @@ class Crypto (object): key = None cnt = None # file counter (uint32_t != 0) iv = None # current IV - pfx = None # accu for 64 bit fixed parts of IV + fixed = None # accu for 64 bit fixed parts of IV password = None paramversion = None stats = { "in" : 0 @@ -374,10 +386,13 @@ class Crypto (object): self.cnt = AES_GCM_IV_CNT_DATA return if cnt == 0 or cnt > AES_GCM_IV_CNT_MAX + 1: - raise Exception ("XXX invalid counter value %d requested" % cnt) + raise InvalidParameter ("invalid counter value %d requested: " + "acceptable values are from 1 to %d" + % (cnt, AES_GCM_IV_CNT_MAX)) if cnt == AES_GCM_IV_CNT_INFOFILE: if self.info_counter_used is True: - raise Exception ("XXX attempted to reuse info file counter") + raise InvalidParameter ("attempted to reuse info file counter " + "%d: must be unique" % cnt) self.info_counter_used = True return if cnt <= AES_GCM_IV_CNT_MAX: @@ -437,17 +452,18 @@ class Encrypt (Crypto): def __init__ (self, password, version, paramversion, nacl=None, counter=AES_GCM_IV_CNT_DATA): if len (password) == 0: - raise Exception ("XXX refusing to encrypt with empty password") - self.pfx = [ ] + raise InvalidParameter ("__init__: refusing to encrypt with empty " + "password") + self.fixed = [ ] self.version = version self.paramenc = ENCRYPTION_PARAMETERS.get (paramversion) ["enc"] super().__init__ (password, paramversion, nacl, counter=counter, - nextpfx=lambda: self.pfx.append (os.urandom(8))) + nextpfx=lambda: self.fixed.append (os.urandom(8))) def iv_make (self): - return struct.pack(FMT_I2N_IV, self.pfx [-1], self.cnt) + return struct.pack(FMT_I2N_IV, self.fixed [-1], self.cnt) def next (self, filename, counter=None): @@ -461,8 +477,8 @@ class Encrypt (Crypto): elif self.paramenc == "passthrough": self.enc = PassthroughCipher () else: - raise Exception ("XXX garbage encryption parameter %d → %r" - % (self.paramversion, enc)) + raise InvalidParameter ("next: parameter version %d not known" + % self.paramversion) hdrdum = hdr_make_dummy (filename) self.lastinfo = (filename, hdrdum) super().next (self.password, self.paramversion, self.nacl) @@ -474,15 +490,18 @@ class Encrypt (Crypto): def done (self, cmpdata): filename, hdrdum = self.lastinfo if cmpdata != hdrdum: - raise Exception ("XXX bad sync for writing header") ## we need to converge on a sensible error handling strategy + raise RuntimeError ("done: bad sync of header for object %d: " + "preliminary data does not match; this likely " + "indicates a wrongly repositioned stream" + % self.cnt) data = self.enc.finalize () self.stats ["out"] += len (data) self.ctsize += len (data) ok, hdr = hdr_from_params (self.version, self.paramversion, self.nacl, self.iv, self.ctsize, self.enc.tag) if ok is False: - raise Exception ("XXX error constructing header: %r" % hdr) ## we need to converge on a sensible error handling strategy - return data, hdr, self.pfx + raise InternalError ("error constructing header: %r" % hdr) + return data, hdr, self.fixed def process (self, buf): @@ -499,17 +518,17 @@ class Decrypt (Crypto): def __init__ (self, password, paramversion=None, nacl=None, counter=None, fixedparts=None): if fixedparts is not None: - self.pfx = fixedparts - self.pfx.sort () + self.fixed = fixedparts + self.fixed.sort () super().__init__ (password, paramversion, nacl, counter=counter) super().__init__ (password, paramversion, nacl, counter=counter) - def valid_pfx (self, iv): + def valid_fixed_part (self, iv): # check if fixed part is known - pfx, _cnt = struct.unpack (FMT_I2N_IV, iv) - i = bisect.bisect_left (self.pfx, pfx) - return i != len (self.pfx) and self.pfx [i] == pfx + fixed, _cnt = struct.unpack (FMT_I2N_IV, iv) + i = bisect.bisect_left (self.fixed, fixed) + return i != len (self.fixed) and self.fixed [i] == fixed def next (self, hdr): @@ -519,12 +538,16 @@ class Decrypt (Crypto): if self.key is None: super().next (self.password, paramversion, hdr ["nacl"]) iv = hdr ["iv"] - if self.pfx is not None and self.valid_pfx (iv) is False: + if self.fixed is not None and self.valid_fixed_part (iv) is False: fixed, _ = struct.unpack (FMT_I2N_IV, iv) raise InvalidIVFixedPart ("iv [%r] has invalid fixed part [%r]" % (iv, fixed)) self.tag = hdr ["tag"] - defs = ENCRYPTION_PARAMETERS.get(paramversion) + defs = ENCRYPTION_PARAMETERS.get (paramversion, None) + if defs is None: + raise FormatError ("header contains unknown parameter version %d; " + "maybe the file was created by a more recent " + "version of Deltatar" % paramversion) enc = defs ["enc"] if enc == "aes-gcm": self.enc = Cipher \ @@ -535,8 +558,8 @@ class Decrypt (Crypto): elif enc == "passthrough": self.enc = PassthroughCipher () else: - raise Exception ("XXX garbage encryption parameter %d → %r" - % (paramversion, enc)) + raise InternalError ("encryption parameter set %d refers to unknown " + "mode %r" % (paramversion, enc)) self.set_object_counter (self.cnt + 1)