self.key_length = key_length
         self.password = password
         self.last_block_offset = 0L
-        self.dbuf = ""
+        self.dbuf     = ""
+        self.aes_buf  = ""
 
         if comptype == "gz":
             try:
             self.internal_pos += len(buf)
         t = "".join(t)
         self.buf = t[size:]
+
         return t[:size]
 
     def __dec_read(self, size):
-        buf = self.fileobj.read(size)
-        last = len(buf) < size
+        '''
+        This function reads directly from the file and returns the data
+        decrypted. This means that if the file is not encrypted, this function
+        is trivial.
+
+        If the data in the file is encrypted, then the process is different:
+        first we have to read the raw encrypted data, then decrypt it and
+        return. But the decryption process is not straightforward because the
+        self.fileobj stream contains multiple encrypted files one after the
+        other. We need to detect each separate file, which is detected because
+        they are separated by the "Salted__" keyword.
+
+        It gets more complicated, because we decrypt chunk by chunk, and to
+        correctly decrypt one chunk we need to set a "last" variable that
+        specifies if it's the last chunk of a file, because the end of a file is
+        handled differently, as it gets padded.
+
+        Knowing if the current chunk is the last part of a file is usually done
+        just by detecting if it's followed by a "Salted__" keyword or if we
+        cannot read more bytes from the stream. BUT there's a pretty particular
+        case, in which the current chunk ends exactly with one file, so that
+        the next chunk starts with "Salted__".
+
+        To fix that rare case, we just read N bytes from the stream, and check
+        if the last bytes correspond with the string "Salted__". Then we save
+        those last characters for next call to __dec_read. If the last bytes
+        were "Salted__", then we set "last" to True.
+
+        Well, actually we not only substract the length of "Salted__", but 16/32
+        chars because the file is decrypted in multiples of the key size.
+        '''
         if self.enctype == 'aes':
-            buf = self.__split_enc_file(buf, last)
-        return buf
+            kl = self.key_length/8
+            buf = self.fileobj.read(size - kl)
+            last = len(buf) < (size - kl)
+            buf = self.aes_buf + buf
+            self.aes_buf = ""
+
+            # prevent setting last to False when it shouldn't
+            if not last:
+                last = buf[-kl:].startswith('Salted__')
+                self.aes_buf = buf[-kl:]
+                buf = buf[:-kl]
+
+            return self.__split_enc_file(buf, last)
+        else:
+            return self.fileobj.read(size)
 
     def __split_enc_file(self, buf, last):
         if not buf:
                 buf = self.encryption.decrypt(b1, True)
             else:
                 buf = ''
+
             self.encryption.get_salt_str(b2)
             self.encryption.init()
             b2 = b2[len(self.encryption.salt_str):]
         else:
             raise StopIteration
         self.index += 1
+
         return tarinfo
 
 # Helper classes for sparse file support