summaryrefslogtreecommitdiff
path: root/source/pdf/pdf-write.c
diff options
context:
space:
mode:
authorRobin Watts <robin.watts@artifex.com>2017-12-07 18:31:37 +0000
committerRobin Watts <robin.watts@artifex.com>2018-01-05 11:47:08 +0000
commitfd0bf575229a79bc22901b0bd8ba4dbd356faa22 (patch)
tree3041da3cb23e3df8fda8f7f9cb0f8fdcdb94a626 /source/pdf/pdf-write.c
parent25593f4f9df0c4a9b9adaa84aaa33fe2a89087f6 (diff)
downloadmupdf-fd0bf575229a79bc22901b0bd8ba4dbd356faa22.tar.xz
Enable saving of encrypted PDF files.
We need both RC4 and AES encryption. RC4 is a straight reversable stream, and our AES library knows how to encrypt as well as decrypt, so it's "just" a matter of calling them correctly. We therefore expose a generic "encrypt this data" routine (and a matching "how long will the data be once encrypted" routine) within pdf-crypt.c. We then extend our our PDF object output routines to call these. This is enough to get encrypted data preserved over calls to mutool clean. Unfortunately the created files aren't readable, due to 2 further problems, also fixed here. Firstly, mutool clean does not preserve the Encrypt entry in the trailer. This is a simple fix. Secondly, we are required NOT to encrypt the Encrypt entry. This requires us to spot the crypt entry and to special case it.
Diffstat (limited to 'source/pdf/pdf-write.c')
-rw-r--r--source/pdf/pdf-write.c37
1 files changed, 28 insertions, 9 deletions
diff --git a/source/pdf/pdf-write.c b/source/pdf/pdf-write.c
index f8b6a5d1..b13970b9 100644
--- a/source/pdf/pdf-write.c
+++ b/source/pdf/pdf-write.c
@@ -93,6 +93,7 @@ struct pdf_write_state_s
pdf_obj *hints_length;
int page_count;
page_objects_list *page_object_lists;
+ int crypt_object_number;
};
/*
@@ -1657,6 +1658,11 @@ static fz_buffer *deflatebuf(fz_context *ctx, const unsigned char *p, size_t n)
return buf;
}
+static void write_data(fz_context *ctx, void *arg, const unsigned char *data, int len)
+{
+ fz_write_data(ctx, (fz_output *)arg, data, len);
+}
+
static void copystream(fz_context *ctx, pdf_document *doc, pdf_write_state *opts, pdf_obj *obj_orig, int num, int gen, int do_deflate)
{
fz_buffer *buf, *tmp;
@@ -1701,13 +1707,13 @@ static void copystream(fz_context *ctx, pdf_document *doc, pdf_write_state *opts
addhexfilter(ctx, doc, obj);
}
- newlen = pdf_new_int(ctx, doc, (int)len);
+ newlen = pdf_new_int(ctx, doc, pdf_encrypted_len(ctx, doc->crypt, num, gen, (int)len));
pdf_dict_put_drop(ctx, obj, PDF_NAME_Length, newlen);
fz_write_printf(ctx, opts->out, "%d %d obj\n", num, gen);
- pdf_print_obj(ctx, opts->out, obj, opts->do_tight);
+ pdf_print_encrypted_obj(ctx, opts->out, obj, opts->do_tight, doc->crypt, num, gen);
fz_write_string(ctx, opts->out, "\nstream\n");
- fz_write_data(ctx, opts->out, data, len);
+ pdf_encrypt_data(ctx, doc->crypt, num, gen, write_data, opts->out, data, len);
fz_write_string(ctx, opts->out, "\nendstream\nendobj\n\n");
fz_drop_buffer(ctx, buf);
@@ -1767,7 +1773,7 @@ static void expandstream(fz_context *ctx, pdf_document *doc, pdf_write_state *op
pdf_dict_put_drop(ctx, obj, PDF_NAME_Length, newlen);
fz_write_printf(ctx, opts->out, "%d %d obj\n", num, gen);
- pdf_print_obj(ctx, opts->out, obj, opts->do_tight);
+ pdf_print_encrypted_obj(ctx, opts->out, obj, opts->do_tight, doc->crypt, num, gen);
fz_write_string(ctx, opts->out, "\nstream\n");
fz_write_data(ctx, opts->out, data, len);
fz_write_string(ctx, opts->out, "\nendstream\nendobj\n\n");
@@ -1845,7 +1851,7 @@ static int is_xml_metadata(fz_context *ctx, pdf_obj *obj)
return 0;
}
-static void writeobject(fz_context *ctx, pdf_document *doc, pdf_write_state *opts, int num, int gen, int skip_xrefs)
+static void writeobject(fz_context *ctx, pdf_document *doc, pdf_write_state *opts, int num, int gen, int skip_xrefs, int unenc)
{
pdf_xref_entry *entry;
pdf_obj *obj;
@@ -1892,13 +1898,13 @@ static void writeobject(fz_context *ctx, pdf_document *doc, pdf_write_state *opt
if (!pdf_obj_num_is_stream(ctx, doc, num))
{
fz_write_printf(ctx, opts->out, "%d %d obj\n", num, gen);
- pdf_print_obj(ctx, opts->out, obj, opts->do_tight);
+ pdf_print_encrypted_obj(ctx, opts->out, obj, opts->do_tight, unenc ? NULL : doc->crypt, num, gen);
fz_write_string(ctx, opts->out, "\nendobj\n\n");
}
else if (entry->stm_ofs < 0 && entry->stm_buf == NULL)
{
fz_write_printf(ctx, opts->out, "%d %d obj\n", num, gen);
- pdf_print_obj(ctx, opts->out, obj, opts->do_tight);
+ pdf_print_encrypted_obj(ctx, opts->out, obj, opts->do_tight, doc->crypt, num, gen);
fz_write_string(ctx, opts->out, "\nstream\nendstream\nendobj\n\n");
}
else
@@ -2018,6 +2024,10 @@ static void writexref(fz_context *ctx, pdf_document *doc, pdf_write_state *opts,
obj = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_ID);
if (obj)
pdf_dict_put(ctx, trailer, PDF_NAME_ID, obj);
+
+ obj = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Encrypt);
+ if (obj)
+ pdf_dict_put(ctx, trailer, PDF_NAME_Encrypt, obj);
}
if (main_xref_offset != 0)
{
@@ -2027,6 +2037,7 @@ static void writexref(fz_context *ctx, pdf_document *doc, pdf_write_state *opts,
}
fz_write_string(ctx, opts->out, "trailer\n");
+ /* Trailer is NOT encrypted */
pdf_print_obj(ctx, opts->out, trailer, opts->do_tight);
fz_write_string(ctx, opts->out, "\n");
@@ -2155,7 +2166,7 @@ static void writexrefstream(fz_context *ctx, pdf_document *doc, pdf_write_state
pdf_update_stream(ctx, doc, dict, fzbuf, 0);
- writeobject(ctx, doc, opts, num, 0, 0);
+ writeobject(ctx, doc, opts, num, 0, 0, 0);
fz_write_printf(ctx, opts->out, "startxref\n%lu\n%%%%EOF\n", startxref);
}
fz_always(ctx)
@@ -2212,7 +2223,7 @@ dowriteobject(fz_context *ctx, pdf_document *doc, pdf_write_state *opts, int num
if (!opts->do_incremental || pdf_xref_is_incremental(ctx, doc, num))
{
opts->ofs_list[num] = fz_tell_output(ctx, opts->out);
- writeobject(ctx, doc, opts, num, opts->gen_list[num], 1);
+ writeobject(ctx, doc, opts, num, opts->gen_list[num], 1, num == opts->crypt_object_number);
}
}
else
@@ -2932,6 +2943,14 @@ do_pdf_save_document(fz_context *ctx, pdf_document *doc, pdf_write_state *opts,
if (opts->do_garbage >= 2 || opts->do_linear)
compactxref(ctx, doc, opts);
+ opts->crypt_object_number = 0;
+ if (doc->crypt)
+ {
+ pdf_obj *crypt = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME_Encrypt);
+ int crypt_num = pdf_to_num(ctx, crypt);
+ opts->crypt_object_number = opts->renumber_map[crypt_num];
+ }
+
/* Make renumbering affect all indirect references and update xref */
if (opts->do_garbage >= 2 || opts->do_linear)
renumberobjs(ctx, doc, opts);