return True
+ def _read_encrypt (self, buf):
+ """
+ Demote a program error to a decryption error in tolerant mode. This
+ allows recovery from corrupted headers and invalid data.
+ """
+ try:
+ return self.encryption.process (buf)
+ except RuntimeError as exn:
+ if self.tolerant is True:
+ raise DecryptionError (exn)
+ raise
+
+
def _finalize_read_encrypt (self):
"""
Finalize decryption.
self.close(close_fileobj=False)
try:
self._init_read_gz()
+ except DecryptionError:
+ if self.tolerant is True:
+ # return whatever data was processed successfully
+ if len (buf) > 0:
+ t.append (buf)
+ if len (t) > 0:
+ break
+ raise
except EndOfFile:
# happens at the end of the file
pass
finalized object is returned.
"""
c = len(self.buf)
- t = [self.buf]
+ t = [self.buf] if c > 0 else []
good_crypto = len (t)
+
while c < size:
todo = size
- if self.arcmode & ARCMODE_ENCRYPT:
- if self.remainder <= 0:
- # prepare next object
- try:
+ try:
+ if self.arcmode & ARCMODE_ENCRYPT:
+ if self.remainder <= 0:
+ # prepare next object
if self._init_read_encrypt () is False: # EOF
buf = None
break # while
- except DecryptionError:
- if self.tolerant is True:
- self.buf = b"".join (t [good_crypto:])
- return b"".join (t [:good_crypto])
- raise
-
- # only read up to the end of the encrypted object
- todo = min (size, self.remainder)
- buf = self.fileobj.read(todo)
- if self.arcmode & ARCMODE_ENCRYPT:
- # decrypt the thing
- buf = self.encryption.process (buf)
- if todo == self.remainder:
- # at the end of a crypto object; finalization will fail if
- # the GCM tag does not match
- try:
+
+ # only read up to the end of the encrypted object
+ todo = min (size, self.remainder)
+ buf = self.fileobj.read(todo)
+ if self.arcmode & ARCMODE_ENCRYPT:
+ # decrypt the thing
+ buf = self._read_encrypt (buf)
+ if todo == self.remainder:
+ # at the end of a crypto object; finalization will fail if
+ # the GCM tag does not match
trailing = self._finalize_read_encrypt ()
- except DecryptionError as exn:
- if self.tolerant is False:
- raise
- if good_crypto == 0:
- raise
- # some objects did validate; discard all data after it;
- # next call will start with the bad object and error
- # out immediately
- self.buf = b"".join (t [good_crypto:])
- return b"".join (t [:good_crypto])
- good_crypto = len (t) + 1
- if len (trailing) > 0:
- buf += trailing
- self.remainder = 0
- else:
- self.remainder -= todo
+ good_crypto = len (t) + 1
+ if len (trailing) > 0:
+ buf += trailing
+ self.remainder = 0
+ else:
+ self.remainder -= todo
+ except DecryptionError:
+ if self.tolerant is False:
+ raise
+ self.encryption.drop ()
+ if good_crypto == 0:
+ raise
+ # this may occur at any of the three crypto operations above.
+ # some objects did validate; discard all data after it; next
+ # call will start with the bad object and error out immediately
+ self.buf = b"".join (t [good_crypto:])
+ return b"".join (t [:good_crypto])
if not buf: ## XXX stream terminated prematurely; this should be an error
break