summaryrefslogtreecommitdiff
path: root/source/pdf/pdf-crypt.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-crypt.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-crypt.c')
-rw-r--r--source/pdf/pdf-crypt.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/source/pdf/pdf-crypt.c b/source/pdf/pdf-crypt.c
index 9f3b05e9..c427e85e 100644
--- a/source/pdf/pdf-crypt.c
+++ b/source/pdf/pdf-crypt.c
@@ -1070,3 +1070,90 @@ pdf_print_crypt(fz_context *ctx, fz_output *out, pdf_crypt *crypt)
fz_write_printf(ctx, out, "}\n");
}
+
+void pdf_encrypt_data(fz_context *ctx, pdf_crypt *crypt, int num, int gen, void (*write_data)(fz_context *ctx, void *, const unsigned char *, int), void *arg, const unsigned char *s, int n)
+{
+ unsigned char buffer[256];
+ unsigned char key[32];
+ int keylen;
+
+ if (crypt == NULL)
+ {
+ write_data(ctx, arg, s, n);
+ return;
+ }
+
+ keylen = pdf_compute_object_key(crypt, &crypt->strf, num, gen, key, 32);
+
+ if (crypt->strf.method == PDF_CRYPT_RC4)
+ {
+ fz_arc4 arc4;
+ fz_arc4_init(&arc4, key, keylen);
+ while (n > 0)
+ {
+ int len = n;
+ if (len > sizeof(buffer))
+ len = sizeof(buffer);
+ fz_arc4_encrypt(&arc4, buffer, s, len);
+ write_data(ctx, arg, buffer, len);
+ s += len;
+ n -= len;
+ }
+ return;
+ }
+
+ if (crypt->strf.method == PDF_CRYPT_AESV2 || crypt->strf.method == PDF_CRYPT_AESV3)
+ {
+ fz_aes aes;
+ unsigned char iv[16];
+
+ /* Empty strings can be represented by empty strings */
+ if (n == 0)
+ return;
+
+ if (fz_aes_setkey_enc(&aes, key, keylen * 8))
+ fz_throw(ctx, FZ_ERROR_GENERIC, "AES key init failed (keylen=%d)", keylen * 8);
+
+ fz_memrnd(ctx, iv, 16);
+ write_data(ctx, arg, iv, 16);
+
+ while (n > 0)
+ {
+ int len = n;
+ if (len > 16)
+ len = 16;
+ memcpy(buffer, s, len);
+ if (len != 16)
+ memset(&buffer[len], 16-len, 16-len);
+ fz_aes_crypt_cbc(&aes, FZ_AES_ENCRYPT, 16, iv, buffer, buffer+16);
+ write_data(ctx, arg, buffer+16, 16);
+ s += 16;
+ n -= 16;
+ }
+ if (n == 0) {
+ memset(buffer, 16, 16);
+ fz_aes_crypt_cbc(&aes, FZ_AES_ENCRYPT, 16, iv, buffer, buffer+16);
+ write_data(ctx, arg, buffer+16, 16);
+ }
+ return;
+ }
+
+ /* Should never happen, but... */
+ write_data(ctx, arg, s, n);
+}
+
+int pdf_encrypted_len(fz_context *ctx, pdf_crypt *crypt, int num, int gen, int len)
+{
+ if (crypt == NULL)
+ return len;
+
+ if (crypt->strf.method == PDF_CRYPT_AESV2 || crypt->strf.method == PDF_CRYPT_AESV3)
+ {
+ len += 16; /* 16 for IV */
+ if ((len & 15) == 0)
+ len += 16; /* Another 16 if our last block is full anyway */
+ len = (len + 15) & ~15; /* And pad to the block */
+ }
+
+ return len;
+}