def __read(self, size):
- """Return size bytes from stream. If internal buffer is empty,
- read another block from the stream.
+ """
+ Return size bytes from stream. If internal buffer is empty, read
+ another block from the stream.
+
+ The function returns up to size bytes of data. When an error occurs
+ during decryption, everything until the end of the last successfully
+ finalized object is returned.
"""
c = len(self.buf)
t = [self.buf]
+ good_crypto = 0
while c < size:
todo = size
if self.arcmode & ARCMODE_ENCRYPT:
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 ()
+ try:
+ trailing = self._finalize_read_encrypt ()
+ except DecryptionError as exn:
+ 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