When opening a document a non-decrypted encryption dict/ID are read, then
the rest of the document. If a repair is triggered it used to throw away
all cached objects to avoid caching non-decrypted strings/streams.
This is the correct decision of all objects except the encryption dict/ID.
If at a later point the PDF document is saved with encryption kept then
all objects will be saved using the encryption key from the non-decrypted
encryption dictionary/ID determined when opening the document, but when
saving the encryption dictionary itself it will not get any special
treatment, thus its owner/user password strings are decrypted. This
decrypted encryption dict and ID are then saved to the output file.
This means that the owner/user password strings no longer correspond
to the ones in the original document. So when mupdf-gl tries to reopen
the output file it will ask for a password, but the original owner/user
password's will not work.
This is fixed by caching the encryption dictionary and ID in non-decrypted
form after repairing the document and clearing out all the non-decrypted
objects. Doing that leaves the objects in memory until it is time to save
the document, causing the encryption dictionary to be saved in non-decrypted
form, which means that the owner/user password strings are left unchanged,
and thus mupdf-gl will accept the original owner/user passwords when reopening
the file.
For the file in this particular bug the owner password is unknown while the
user password is the default password (i.e. the empty string).
{
/* ensure that strings are not used in their repaired, non-decrypted form */
if (doc->crypt)
+ {
+ pdf_crypt *tmp;
pdf_clear_xref(ctx, doc);
+
+ /* ensure that Encryption dictionary and ID are cached without decryption,
+ otherwise a decrypted Encryption dictionary and ID may be used when saving
+ the PDF causing it to be inconsistent (since strings/streams are encrypted
+ with the actual encryption key, not the decrypted encryption key). */
+ tmp = doc->crypt;
+ doc->crypt = NULL;
+ fz_try(ctx)
+ {
+ (void) pdf_resolve_indirect(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Encrypt)));
+ (void) pdf_resolve_indirect(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(ID)));
+ }
+ fz_always(ctx)
+ doc->crypt = tmp;
+ fz_catch(ctx)
+ {
+ fz_rethrow(ctx);
+ }
+ }
}
fz_catch(ctx)
{