summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'pdf/pdf_sec.c')
-rw-r--r--pdf/pdf_sec.c1418
1 files changed, 1418 insertions, 0 deletions
diff --git a/pdf/pdf_sec.c b/pdf/pdf_sec.c
new file mode 100644
index 00000000..ff608052
--- /dev/null
+++ b/pdf/pdf_sec.c
@@ -0,0 +1,1418 @@
+/* Copyright (C) 2020-2021 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied,
+ modified or distributed except as expressly authorized under the terms
+ of the license contained in the file LICENSE in this distribution.
+
+ Refer to licensing information at http://www.artifex.com or contact
+ Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
+ CA 94945, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* PDF decryption routines */
+
+#include "pdf_stack.h"
+#include "pdf_file.h"
+#include "pdf_dict.h"
+#include "pdf_array.h"
+#include "pdf_sec.h"
+#include "pdf_misc.h"
+#include "strmio.h"
+#include "smd5.h"
+#include "sarc4.h"
+#include "aes.h"
+#include "sha2.h"
+#include "pdf_utf8.h"
+#include "pdf_deref.h"
+
+/* The padding string as defined in step 1 of Algorithm 3.2 */
+static char PadString[32] = {
+ 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08,
+ 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a
+};
+
+/* If EncryptMetadata is true we need to add 4 bytes of 0xFF to the MD5 hash
+ * when computing an encryption key (Algorithm 3.2, step 6 when R is 4 or more)
+ */
+static char R4String[4] = {
+ 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* If using the AES filter, we need to add this to the encryption
+ * key when creating the key for decrypting objects (streams or strings)
+ */
+static char sAlTString[4] = {
+ 0x73, 0x41, 0x6c, 0x54
+};
+
+static int pdf_compute_encryption_key_preR5(pdf_context *ctx, char *Password, int PasswordLen, int KeyLen, pdf_string **EKey, int R)
+{
+ char Key[32];
+ int code = 0, KeyLenBytes = KeyLen / 8, i;
+ char P[4];
+ gs_md5_state_t md5;
+ pdf_array *a = NULL;
+ pdf_string *s = NULL;
+
+ *EKey = NULL;
+ /* Algorithm 3.2 */
+ /* Step 1. Pad or truncate the password string to exactly 32 bytes
+ * using the defined padding string.
+ */
+ if (PasswordLen > 32)
+ PasswordLen = 32;
+
+ memcpy(Key, Password, PasswordLen);
+
+ if (PasswordLen < 32)
+ memcpy(&Key[PasswordLen], PadString, 32 - PasswordLen);
+
+ /* 2. Initialise the MD5 hash function and pass the result of step 1 to this function */
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, (gs_md5_byte_t *)&Key, 32);
+
+ /* 3. Pass the value of the encryption dictionary's O entry to the MD5 hash */
+ gs_md5_append(&md5, (gs_md5_byte_t *)ctx->encryption.O, 32);
+
+ /* 4. Treat the value of P as an unsigned 4 byte integer and pass those bytes to the MD5 */
+ P[3] = ((uint32_t)ctx->encryption.P) >> 24;
+ P[2] = ((uint32_t)ctx->encryption.P & 0x00ff0000) >> 16;
+ P[1] = ((uint32_t)ctx->encryption.P & 0x0000ff00) >> 8;
+ P[0] = ((uint32_t)ctx->encryption.P & 0xff);
+ gs_md5_append(&md5, (gs_md5_byte_t *)P, 4);
+
+ /* 5. Pass the first element of the file's file identifier array */
+ code = pdfi_dict_get_type(ctx, ctx->Trailer, "ID", PDF_ARRAY, (pdf_obj **)&a);
+ if (code < 0) {
+ if (code == gs_error_undefined) {
+ emprintf(ctx->memory, "\n **** Error: ID key in the trailer is required for encrypted files.\n");
+ emprintf(ctx->memory, " File may not be possible to decrypt.\n");
+ } else
+ return code;
+ }
+ /* If the file ID was missing, just ignore the error */
+ if (code == 0) {
+ code = pdfi_array_get_type(ctx, a, (uint64_t)0, PDF_STRING, (pdf_obj **)&s);
+ if (code < 0)
+ goto done;
+ gs_md5_append(&md5, s->data, s->length);
+ }
+
+ /* Step 6
+ * (revision 4 or greater) If document Metadata is not being encrypted
+ * pass 4 bytes with the value 0xFFFFFFFF to the MD5 hash function.
+ */
+ if (R > 3 && !ctx->encryption.EncryptMetadata) {
+ gs_md5_append(&md5, (const gs_md5_byte_t *)R4String, 4);
+ }
+
+ /* 7. Finish the hash */
+ gs_md5_finish(&md5, (gs_md5_byte_t *)&Key);
+
+ code = pdfi_object_alloc(ctx, PDF_STRING, KeyLenBytes, (pdf_obj **)EKey);
+ if (code < 0)
+ goto done;
+ pdfi_countup((pdf_obj *)*EKey);
+
+ /* Step 8
+ * (revision 3 or greater) Do the following 50 times. Take the output from the
+ * previous MD5 hash and pass hte first n bytes of the output as input to a new
+ * MD5 hash, where n is the number of bytes of the encryption key as defined by
+ * the value of the encryption dictionary's Length entry. (NB Length is in bits)
+ */
+ if (R > 2) {
+ for (i=0;i < 50; i++) {
+ memcpy((*EKey)->data, Key, KeyLenBytes);
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, (gs_md5_byte_t *)(*EKey)->data, KeyLenBytes);
+ gs_md5_finish(&md5, (gs_md5_byte_t *)&Key);
+ }
+ }
+
+ /* Step 9
+ * Set the encryption key to the first n bytes of the output from the final MD5 hash
+ * where n is always 5 for revision 2 but, for revision 3 or greater, depends on the
+ * value of the encryption dictionary's Length entry.
+ */
+ memcpy((*EKey)->data, Key, KeyLenBytes);
+
+done:
+ pdfi_countdown(s);
+ pdfi_countdown(a);
+ return code;
+}
+
+#ifdef HAVE_LIBIDN
+# include <stringprep.h>
+
+static int apply_sasl(pdf_context *ctx, char *Password, int Len, char **NewPassword, int *NewLen)
+{
+ byte *buffer;
+ uint buffer_size;
+ Stringprep_rc err;
+
+ buffer_size = Len * 11 + 1;
+ buffer = (byte *)gs_alloc_bytes(ctx->memory, buffer_size, "saslprep result");
+ if (buffer == NULL)
+ return_error(gs_error_VMerror);
+
+ err = stringprep((char *)buffer, buffer_size, 0, stringprep_saslprep);
+ if (err != STRINGPREP_OK) {
+ gs_free_object(ctx->memory, buffer, "saslprep result");
+
+ /* Since we're just verifying the password to an existing
+ * document here, we don't care about "invalid input" errors
+ * like STRINGPREP_CONTAINS_PROHIBITED. In these cases, we
+ * ignore the error and return the original string unchanged --
+ * chances are it's not the right password anyway, and if it
+ * is we shouldn't gratuitously fail to decrypt the document.
+ *
+ * On the other hand, errors like STRINGPREP_NFKC_FAILED are
+ * real errors, and should be returned to the user.
+ *
+ * Fortunately, the stringprep error codes are sorted to make
+ * this easy: the errors we want to ignore are the ones with
+ * codes less than 100. */
+ if ((int)err < 100)
+ return 0;
+
+ return_error(gs_error_ioerror);
+ }
+
+ *NewLen = strlen((char *)buffer);
+ *NewPassword = (char *)buffer;
+
+ return 0;
+
+}
+#endif
+
+static int check_user_password_R5(pdf_context *ctx, char *Password, int Len, int KeyLen)
+{
+ char *UTF8_Password, *Test = NULL, Buffer[32], UEPadded[48];
+ int NewLen;
+ int code = 0;
+ pdf_c_stream *stream = NULL, *filter_stream = NULL;
+ pdf_string *Key = NULL;
+ SHA256_CTX sha256;
+
+ /* Algorithm 3.11 from the Adobe extensions to the ISO 32000 specification (Extension Level 3) */
+ /* Step 1 convert the password to UTF-8 */
+
+ /* From the original code in Resource/Init/pdf_sec.ps:
+ * Step 1.
+ * If the .saslprep operator isn't available (because ghostscript
+ * wasn't built with libidn support), just skip this step. ASCII
+ * passwords will still work fine, and even most non-ASCII passwords
+ * will be okay; any non-ASCII passwords that fail will produce a
+ * warning from pdf_process_Encrypt.
+ */
+#ifdef HAVE_LIBIDN
+ code = apply_sasl(ctx, Password, Len, &UTF8_Password, &NewLen);
+ if (code < 0)
+ return code;
+#else
+ UTF8_Password = Password;
+ NewLen = Len;
+#endif
+ if (NewLen > 127)
+ NewLen = 127;
+
+ Test = (char *)gs_alloc_bytes(ctx->memory, NewLen + 8, "R5 password test");
+ if (Test == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto error;
+ }
+
+ /* Try to validate the password as the user password */
+ /* concatenate the password */
+ memcpy(Test, UTF8_Password, NewLen);
+ /* With the 'User Validation Salt' (stored as part of the /O string */
+ memcpy(&Test[NewLen], &ctx->encryption.U[32], 8);
+
+ pSHA256_Init(&sha256);
+ pSHA256_Update(&sha256, (uint8_t *)Test, NewLen + 8);
+ pSHA256_Final((uint8_t *)Buffer, &sha256);
+
+ if (memcmp(Buffer, ctx->encryption.U, 32) != 0) {
+ code = gs_note_error(gs_error_unknownerror);
+ goto error;
+ }
+
+ /* Password matched */
+ /* Finally calculate the decryption key */
+ gs_free_object(ctx->memory, Test, "R5 password test");
+
+ /* Password + last 8 bytes of /U */
+ Test = (char *)gs_alloc_bytes(ctx->memory, NewLen + 8, "R5 password test");
+ if (Test == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto error;
+ }
+
+ memcpy(Test, UTF8_Password, NewLen);
+ /* The 'User Key Salt' (stored as part of the /O string */
+ memcpy(&Test[NewLen], &ctx->encryption.U[40], 8);
+
+ pSHA256_Init(&sha256);
+ pSHA256_Update(&sha256, (uint8_t *)Test, NewLen + 8);
+ pSHA256_Final((uint8_t *)Buffer, &sha256);
+
+ memset(UEPadded, 0x00, 16);
+ memcpy(&UEPadded[16], ctx->encryption.UE, 32);
+
+ code = pdfi_object_alloc(ctx, PDF_STRING, 32, (pdf_obj **)&Key);
+ if (code < 0)
+ goto error;
+ /* pdfi_object_alloc() creates objects with a refrence count of 0 */
+ pdfi_countup(Key);
+ memcpy(Key->data, Buffer, 32);
+
+ /* Now apply AESDecode to the padded UE string, using the SHA from above as the key */
+ code = pdfi_open_memory_stream_from_memory(ctx, 48, (byte *)UEPadded, &stream, true);
+ if (code < 0)
+ goto error;
+
+ code = pdfi_apply_AES_filter(ctx, Key, false, stream, &filter_stream);
+ if (code < 0) {
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ goto error;
+ }
+
+ sfread(Buffer, 1, 32, filter_stream->s);
+ pdfi_close_file(ctx, filter_stream);
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ pdfi_object_alloc(ctx, PDF_STRING, 32, (pdf_obj **)&ctx->encryption.EKey);
+ if (ctx->encryption.EKey == NULL)
+ goto error;
+ memcpy(ctx->encryption.EKey->data, Buffer, 32);
+ pdfi_countup(ctx->encryption.EKey);
+
+error:
+ pdfi_countdown(Key);
+ gs_free_object(ctx->memory, Test, "R5 password test");
+#ifdef HAVE_LIBIDN
+ gs_free_object(ctx->memory, UTF8_Password, "free sasl result");
+#endif
+ return code;
+}
+
+/* Implementation of the PDF security handler revision6 (PDF 1.7 ExtensionLevel 8 algorithm)
+ *
+ * Adobe/ISO has not yet released the details, so the algorithm reference is:
+ * http://esec-lab.sogeti.com/post/The-undocumented-password-validation-algorithm-of-Adobe-Reader-X
+ *
+ * The code below is the same as (and copied from) the MuPDF implementation. And copied from the
+ * Ghostscript implementation in zpdf_r6.c. The ISO specification is now released and the algorithms
+ * used here are documented in the PDF 2.0 specification ISO 32000-2:2017
+ */
+
+static void
+pdf_compute_hardened_hash_r6(unsigned char *password, int pwlen, unsigned char salt[16], unsigned char *ownerkey, unsigned char hash[32])
+{
+ unsigned char data[(128 + 64 + 48) * 64];
+ unsigned char block[64];
+ int block_size = 32;
+ int data_len = 0;
+ int i, j, sum;
+
+ SHA256_CTX sha256;
+ SHA384_CTX sha384;
+ SHA512_CTX sha512;
+ aes_context aes;
+
+ pSHA256_Init(&sha256);
+ pSHA256_Update(&sha256, password, pwlen);
+ pSHA256_Update(&sha256, salt, 8);
+ if (ownerkey)
+ pSHA256_Update(&sha256, ownerkey, 48);
+ pSHA256_Final((uint8_t *)block, &sha256);
+
+ for (i = 0; i < 64 || i < data[data_len * 64 - 1] + 32; i++)
+ {
+ /* Step 2: repeat password and data block 64 times */
+ memcpy(data, password, pwlen);
+ memcpy(data + pwlen, block, block_size);
+ if (ownerkey)
+ memcpy(data + pwlen + block_size, ownerkey, 48);
+ data_len = pwlen + block_size + (ownerkey ? 48 : 0);
+ for (j = 1; j < 64; j++)
+ memcpy(data + j * data_len, data, data_len);
+
+ /* Step 3: encrypt data using data block as key and iv */
+ aes_setkey_enc(&aes, block, 128);
+ aes_crypt_cbc(&aes, AES_ENCRYPT, data_len * 64, block + 16, data, data);
+
+ /* Step 4: determine SHA-2 hash size for this round */
+ for (j = 0, sum = 0; j < 16; j++)
+ sum += data[j];
+
+ /* Step 5: calculate data block for next round */
+ block_size = 32 + (sum % 3) * 16;
+ switch (block_size)
+ {
+ case 32:
+ pSHA256_Init(&sha256);
+ pSHA256_Update(&sha256, data, data_len * 64);
+ pSHA256_Final((uint8_t *)block, &sha256);
+ break;
+ case 48:
+ pSHA384_Init(&sha384);
+ pSHA384_Update(&sha384, data, data_len * 64);
+ pSHA384_Final((uint8_t *)block, &sha384);
+ break;
+ case 64:
+ pSHA512_Init(&sha512);
+ pSHA512_Update(&sha512, data, data_len * 64);
+ pSHA512_Final((uint8_t *)block, &sha512);
+ break;
+ }
+ }
+
+ memset(data, 0, sizeof(data));
+ memcpy(hash, block, 32);
+}
+
+static void
+pdf_compute_encryption_key_r6(unsigned char *password, int pwlen, unsigned char *O, unsigned char *OE, unsigned char *U, unsigned char *UE, int ownerkey, unsigned char *validationkey, unsigned char *output)
+{
+ unsigned char hash[32];
+ unsigned char iv[16];
+ aes_context aes;
+
+ if (pwlen > 127)
+ pwlen = 127;
+
+ pdf_compute_hardened_hash_r6(password, pwlen,
+ (ownerkey ? O : U) + 32,
+ ownerkey ? U : NULL, validationkey);
+ pdf_compute_hardened_hash_r6(password, pwlen,
+ (ownerkey ? O : U) + 40,
+ (ownerkey ? U : NULL), hash);
+
+ memset(iv, 0, sizeof(iv));
+ aes_setkey_dec(&aes, hash, 256);
+ aes_crypt_cbc(&aes, AES_DECRYPT, 32, iv,
+ ownerkey ? OE : UE, output);
+}
+
+static int check_user_password_R6(pdf_context *ctx, char *Password, int Len, int KeyLen)
+{
+ unsigned char validation[32];
+ unsigned char output[32];
+
+ pdf_compute_encryption_key_r6((unsigned char *)Password, Len, (unsigned char *)ctx->encryption.O, (unsigned char *)ctx->encryption.OE,
+ (unsigned char *)ctx->encryption.U, (unsigned char *)ctx->encryption.UE, 0, validation, output);
+
+ if (memcmp(validation, ctx->encryption.U, 32) != 0)
+ return_error(gs_error_unknownerror);
+
+ pdfi_object_alloc(ctx, PDF_STRING, 32, (pdf_obj **)&ctx->encryption.EKey);
+ if (ctx->encryption.EKey == NULL)
+ return_error(gs_error_VMerror);;
+ memcpy(ctx->encryption.EKey->data, output, 32);
+ pdfi_countup(ctx->encryption.EKey);
+
+ return 0;
+}
+
+static int check_user_password_preR5(pdf_context *ctx, char *Password, int Len, int KeyLen, int R)
+{
+ pdf_string *Key = NULL, *XORKey = NULL;
+ int code = 0, i, j, KeyLenBytes = KeyLen / 8;
+ pdf_c_stream *stream, *arc4_stream;
+ char Buffer[32];
+ char Hash[16];
+ gs_md5_state_t md5;
+ pdf_string *s = NULL;
+ pdf_array *a = NULL;
+
+ /* Algorithm 3.6, step 1
+ * perform all but the last step of Algorithm 3,4 (Revision 2)
+ * or Algorithm 3.5 (revision 3 or greater).
+ */
+
+ /* Algorithm 3.4 step 1
+ * Create an encryption key based on the user password string as described in Algorithm 3.2
+ */
+ code = pdf_compute_encryption_key_preR5(ctx, Password, Len, KeyLen, &Key, R);
+ if (code < 0)
+ return code;
+
+ switch (R) {
+ case 2:
+ /* Algorithm 3.4, step 2
+ * Encrypt the 32 byte padding string from step 1 of Algorithm 3.2, with an RC4
+ * encryption function, using the key from the preceding step.
+ */
+
+ code = pdfi_open_memory_stream_from_memory(ctx, 32, (byte *)PadString, &stream, true);
+ if (code < 0)
+ goto error;
+
+ code = pdfi_apply_Arc4_filter(ctx, Key, stream, &arc4_stream);
+ if (code < 0) {
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ goto error;
+ }
+
+ sfread(Buffer, 1, 32, arc4_stream->s);
+ pdfi_close_file(ctx, arc4_stream);
+ pdfi_close_memory_stream(ctx, NULL, stream);
+
+ /* Algorithm 3.6 step 2
+ * If the result of the step above is equal to the value of the encryption dictionary
+ * U entry the password supplied is the correct user password.
+ */
+ if (memcmp(Buffer, ctx->encryption.U, 32) != 0) {
+ code = gs_error_unknownerror;
+ goto error;
+ } else {
+ /* Password authenticated, we can now use the calculated encryption key to decrypt the file */
+ ctx->encryption.EKey = Key;
+ }
+ break;
+ case 3:
+ case 4:
+ /* Algorithm 3.5 step 2
+ * Pass the 32 byte padding string from step 1 of Algorithm 3.2 to an MD5 hash */
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, (gs_md5_byte_t *)PadString, 32);
+ code = pdfi_dict_get_type(ctx, ctx->Trailer, "ID", PDF_ARRAY, (pdf_obj **)&a);
+ if (code < 0) {
+ if (code == gs_error_undefined) {
+ emprintf(ctx->memory, "\n **** Error: ID key in the trailer is required for encrypted files.\n");
+ emprintf(ctx->memory, " File may not be possible to decrypt.\n");
+ } else
+ return code;
+ }
+ if (code == 0) {
+ /* Step 3
+ * Pass the first element of the file's file identifier array to the hash function
+ * and finish the hash
+ */
+ code = pdfi_array_get_type(ctx, a, (uint64_t)0, PDF_STRING, (pdf_obj **)&s);
+ if (code < 0)
+ goto error;
+ gs_md5_append(&md5, s->data, s->length);
+ }
+ gs_md5_finish(&md5, (gs_md5_byte_t *)&Hash);
+
+ /* Step 4
+ * Encrypt the 16-byte result of the hash using an RC4 encryption function with
+ * the encryption key from step 1 (of Algorithm 3.5).
+ */
+ code = pdfi_open_memory_stream_from_memory(ctx, 16, (byte *)Hash, &stream, true);
+ if (code < 0)
+ goto error;
+
+ code = pdfi_apply_Arc4_filter(ctx, Key, stream, &arc4_stream);
+ if (code < 0) {
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ goto error;
+ }
+
+ sfread(Buffer, 1, 16, arc4_stream->s);
+ pdfi_close_file(ctx, arc4_stream);
+ pdfi_close_memory_stream(ctx, NULL, stream);
+
+ code = pdfi_object_alloc(ctx, PDF_STRING, KeyLenBytes, (pdf_obj **)&XORKey);
+ if (code < 0)
+ goto error;
+ /* pdfi_object_alloc() creates objects with a reference count of 0 */
+ pdfi_countup(XORKey);
+
+ /* Step 5
+ * Do the following 19 times. Take the output from the previous invocation of the RC4
+ * function and pas it as input to a new invocation of the function; use an encryption key
+ * generated by taking each byte of the original encyption key (obtained in step 1 of
+ * algorithm 3.5) and performing an XOR operation between that byte and the single byte
+ * value of the iteration counter (from 1 to 19).
+ */
+ for (i=1;i < 20;i++) {
+ memcpy(Hash, Buffer, 16);
+ code = pdfi_open_memory_stream_from_memory(ctx, 16, (byte *)Hash, &stream, true);
+ if (code < 0)
+ goto error;
+
+ for (j=0;j < KeyLenBytes;j++) {
+ XORKey->data[j] = Key->data[j] ^ i;
+ }
+
+ code = pdfi_apply_Arc4_filter(ctx, XORKey, stream, &arc4_stream);
+ if (code < 0) {
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ goto error;
+ }
+ sfread(Buffer, 1, 16, arc4_stream->s);
+ pdfi_close_file(ctx, arc4_stream);
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ }
+
+ /* Algorithm 3.6 step 2
+ * If the result of the step above is equal to the value of the encryption dictionary U entry
+ * (comparing on the first 16 bytes in the case of revision 3 of greater)
+ * the password supplied is the correct user password.
+ */
+ if (memcmp(Buffer, ctx->encryption.U, 16) != 0) {
+ code = gs_error_unknownerror;
+ goto error;
+ } else {
+ /* Password authenticated, we can now use the calculated encryption key to decrypt the file */
+ ctx->encryption.EKey = Key;
+ }
+ break;
+ default:
+ code = gs_note_error(gs_error_rangecheck);
+ goto error;
+ break;
+ }
+
+ /* We deliberately don't countdown Key here, if we created it and there were no
+ * errors then we will have stored it in the global context for future use. It
+ * will be counted down when the context is destroyed.
+ */
+ pdfi_countdown(XORKey);
+ pdfi_countdown(s);
+ pdfi_countdown(a);
+ return 0;
+
+error:
+ pdfi_countdown(XORKey);
+ pdfi_countdown(Key);
+ pdfi_countdown(s);
+ pdfi_countdown(a);
+ return code;
+}
+
+static int check_owner_password_R5(pdf_context *ctx, char *Password, int Len, int KeyLen)
+{
+ char *UTF8_Password, *Test = NULL, Buffer[32], OEPadded[48];
+ int NewLen;
+ int code = 0;
+ pdf_c_stream *stream = NULL, *filter_stream = NULL;
+ pdf_string *Key = NULL;
+ SHA256_CTX sha256;
+
+ /* From the original code in Resource/Init/pdf_sec.ps:
+ * Step 1.
+ * If the .saslprep operator isn't available (because ghostscript
+ * wasn't built with libidn support), just skip this step. ASCII
+ * passwords will still work fine, and even most non-ASCII passwords
+ * will be okay; any non-ASCII passwords that fail will produce a
+ * warning from pdf_process_Encrypt.
+ */
+#ifdef HAVE_LIBIDN
+ code = apply_sasl(ctx, Password, Len, &UTF8_Password, &NewLen);
+ if (code < 0)
+ return code;
+#else
+ UTF8_Password = Password;
+ NewLen = Len;
+#endif
+ if (NewLen > 127)
+ NewLen = 127;
+
+ Test = (char *)gs_alloc_bytes(ctx->memory, NewLen + 8 + 48, "r5 password test");
+ if (Test == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto error;
+ }
+
+ /* concatenate the password */
+ memcpy(Test, UTF8_Password, NewLen);
+ /* With the 'Owner Validation Salt' (stored as part of the /O string */
+ memcpy(&Test[NewLen], &ctx->encryption.O[32], 8);
+ /* and also concatenated with the /U string, which is defined to be 48 bytes for revision 5 */
+ memcpy(&Test[NewLen + 8], &ctx->encryption.U, 48);
+
+ /* Now calculate the SHA256 hash */
+ pSHA256_Init(&sha256);
+ pSHA256_Update(&sha256, (const uint8_t *)Test, NewLen + 8 + 48);
+ pSHA256_Final((uint8_t *)Buffer, &sha256);
+
+ if (memcmp(Buffer, ctx->encryption.O, 32) != 0) {
+ code = gs_note_error(gs_error_unknownerror);
+ goto error;
+ }
+
+ /* Password matched */
+ /* Finally calculate the decryption key */
+ gs_free_object(ctx->memory, Test, "R5 password test");
+
+ /* Password + last 8 bytes of /O */
+ Test = (char *)gs_alloc_bytes(ctx->memory, NewLen + 8 + 48, "R5 password test");
+ if (Test == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto error;
+ }
+
+ memcpy(Test, UTF8_Password, NewLen);
+ /* The 'User Key Salt' (stored as part of the /O string */
+ memcpy(&Test[NewLen], &ctx->encryption.O[40], 8);
+ memcpy(&Test[NewLen + 8], ctx->encryption.U, 48);
+
+ /* Now calculate the SHA256 hash */
+ pSHA256_Init(&sha256);
+ pSHA256_Update(&sha256, (const uint8_t *)Test, NewLen + 8 + 48);
+ pSHA256_Final((uint8_t *)Buffer, &sha256);
+
+ memset(OEPadded, 0x00, 16);
+ memcpy(&OEPadded[16], ctx->encryption.OE, 32);
+
+ code = pdfi_object_alloc(ctx, PDF_STRING, 32, (pdf_obj **)&Key);
+ if (code < 0)
+ goto error;
+ /* pdfi_object_alloc() creates objects with a refrence count of 0 */
+ pdfi_countup(Key);
+ memcpy(Key->data, Buffer, 32);
+
+ /* Now apply AESDecode to the padded UE string, using the SHA from above as the key */
+ code = pdfi_open_memory_stream_from_memory(ctx, 48, (byte *)OEPadded, &stream, true);
+ if (code < 0)
+ goto error;
+
+ code = pdfi_apply_AES_filter(ctx, Key, false, stream, &filter_stream);
+ if (code < 0) {
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ goto error;
+ }
+
+ sfread(Buffer, 1, 32, filter_stream->s);
+ pdfi_close_file(ctx, filter_stream);
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ pdfi_object_alloc(ctx, PDF_STRING, 32, (pdf_obj **)&ctx->encryption.EKey);
+ if (ctx->encryption.EKey == NULL)
+ goto error;
+ memcpy(ctx->encryption.EKey->data, Buffer, 32);
+ pdfi_countup(ctx->encryption.EKey);
+
+error:
+ pdfi_countdown(Key);
+ gs_free_object(ctx->memory, Test, "R5 password test");
+#ifdef HAVE_LIBIDN
+ gs_free_object(ctx->memory, UTF8_Password, "free sasl result");
+#endif
+ return code;
+}
+
+static int check_owner_password_R6(pdf_context *ctx, char *Password, int Len, int KeyLen)
+{
+ unsigned char validation[32];
+ unsigned char output[32];
+
+ pdf_compute_encryption_key_r6((unsigned char *)Password, Len, (unsigned char *)ctx->encryption.O, (unsigned char *)ctx->encryption.OE,
+ (unsigned char *)ctx->encryption.U, (unsigned char *)ctx->encryption.UE, 1, validation, output);
+
+ if (memcmp(validation, ctx->encryption.O, 32) != 0)
+ return_error(gs_error_unknownerror);
+
+ pdfi_object_alloc(ctx, PDF_STRING, 32, (pdf_obj **)&ctx->encryption.EKey);
+ if (ctx->encryption.EKey == NULL)
+ return_error(gs_error_VMerror);;
+ memcpy(ctx->encryption.EKey->data, output, 32);
+ pdfi_countup(ctx->encryption.EKey);
+
+ return 0;
+}
+
+static int check_owner_password_preR5(pdf_context *ctx, char *Password, int Len, int KeyLen, int R)
+{
+ char Key[32];
+ int code = 0, i, j, KeyLenBytes = KeyLen / 8;
+ pdf_string *EKey = NULL;
+ gs_md5_state_t md5;
+ pdf_c_stream *stream, *arc4_stream;
+ char Buffer[32], Arc4Source[32];
+
+ /* Algorithm 3.7 */
+ /* Step 1, Compute an encryption key from steps 1-4 of Algorithm 3.3 */
+
+ /* Algorithm 3.3, step 1. Pad or truncate the password string to exactly 32 bytes */
+ if (Len > 32)
+ Len = 32;
+
+ memcpy(Key, Password, Len);
+
+ if (Len < 32)
+ memcpy(&Key[Len], PadString, 32 - Len);
+
+ /* Algorithm 3.3, step 2. Initialise the MD5 hash function and pass the result of step 1 to this function */
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, (gs_md5_byte_t *)&Key, 32);
+ gs_md5_finish(&md5, (gs_md5_byte_t *)&Key);
+
+ /* Algorithm 3.3, step 3. Only for R3 or greater */
+ if (R > 2) {
+ code = pdfi_object_alloc(ctx, PDF_STRING, KeyLenBytes, (pdf_obj **)&EKey);
+ if (code < 0)
+ goto error;
+ /* pdfi_object_alloc() creates objects with a refrence count of 0 */
+ pdfi_countup(EKey);
+
+ for (i = 0; i < 50; i++) {
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, (gs_md5_byte_t *)&Key, KeyLenBytes);
+ gs_md5_finish(&md5, (gs_md5_byte_t *)&Key);
+ }
+ /* Algorithm 3.3, step 4. Use KeyLen bytes of the final hash as an RC$ key */
+ /* Algorithm 3.7, step 2 (R >= 3) */
+ memcpy(Buffer, ctx->encryption.O, 32);
+
+ /* Algorithm 3.7 states (at the end):
+ * "performing an XOR (exclusive or) operation between each byte of the key and the single-byte value of the iteration counter (from 19 to 0)."
+ * which implies that the loop should run 20 times couting down from 19 to 0. For decryption at least this is completely
+ * incorrect. Doing that results in completely garbage output.
+ * By using 0 as the first index we get the correct Key when XOR'ing that with the
+ * key computed above, and continuing until the loop counter reaches 19 gives us the correct
+ * result.
+ */
+ for (i=0; i<20; i++) {
+ memcpy(Arc4Source, Buffer, 32);
+ code = pdfi_open_memory_stream_from_memory(ctx, 32, (byte *)Arc4Source, &stream, true);
+ if (code < 0)
+ goto error;
+ for(j=0;j< KeyLenBytes;j++){
+ EKey->data[j] = Key[j] ^ i;
+ }
+ code = pdfi_apply_Arc4_filter(ctx, EKey, stream, &arc4_stream);
+ sfread(Buffer, 1, 32, arc4_stream->s);
+ pdfi_close_file(ctx, arc4_stream);
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ }
+
+ } else {
+ /* Algorithm 3.3, step 4. For revision 2 always use 5 bytes of the final hash as an RC4 key */
+ code = pdfi_object_alloc(ctx, PDF_STRING, 5, (pdf_obj **)&EKey);
+ if (code < 0)
+ goto error;
+ pdfi_countup(EKey);
+ memcpy(EKey->data, Key, 5);
+
+ /* Algorithm 3.7, step 2 (R == 2) Use RC4 with the computed key to decrypt the O entry of the crypt dict */
+ code = pdfi_open_memory_stream_from_memory(ctx, 32, (byte *)ctx->encryption.O, &stream, true);
+ if (code < 0)
+ goto error;
+
+ code = pdfi_apply_Arc4_filter(ctx, EKey, stream, &arc4_stream);
+ pdfi_countdown(EKey);
+ EKey = NULL;
+
+ sfread(Buffer, 1, 32, arc4_stream->s);
+
+ pdfi_close_file(ctx, arc4_stream);
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ }
+
+ /* Algorithm 3.7, step 3, the result of step 2 purports to be the user password, check it */
+ code = check_user_password_preR5(ctx, Buffer, 32, KeyLen, R);
+
+error:
+ pdfi_countdown(EKey);
+ return code;
+}
+
+/* Compute a decryption key for an 'object'. The decryption key for a string or stream is
+ * calculated by algorithm 3.1.
+ */
+int pdfi_compute_objkey(pdf_context *ctx, pdf_obj *obj, pdf_string **Key)
+{
+ char *Buffer;
+ int idx, ELength, code = 0, md5_length = 0;
+ gs_md5_state_t md5;
+ int64_t object_num;
+ uint32_t generation_num;
+
+ if (ctx->encryption.R < 5) {
+ if (obj->object_num == 0) {
+ /* The object is a direct object, use the object number of the container instead */
+ object_num = obj->indirect_num;
+ generation_num = obj->indirect_gen;
+ } else {
+ object_num = obj->object_num;
+ generation_num = obj->generation_num;
+ }
+
+ /* Step 1, obtain the object and generation numbers (see arguments). If the string is
+ * a direct object, use the identifier of the indirect object containing it.
+ * Buffer length is a maximum of the Encryption key + 3 bytes from the object number
+ * + 2 bytes from the generation number and (for AES filters) 4 bytes of sALT.
+ * But... We must make sure the buffer is large enough for the 128 bits of an MD5 hash.
+ */
+ md5_length = ctx->encryption.EKey->length + 9;
+ if (md5_length < 16)
+ md5_length = 16;
+
+ Buffer = (char *)gs_alloc_bytes(ctx->memory, md5_length, "");
+ if (Buffer == NULL)
+ return gs_note_error(gs_error_VMerror);
+
+ /* Step 2, Treating the object number and generation number as binary integers, extend
+ * the original n-byte encryption key (calculated in pdfi_read_Encryption) to n+5 bytes
+ * by appending the low order 3 bytes of the object number and the low order 2 bytes of
+ * the generation number in that order, low-order byte first. (n is 5 unless the value
+ * of V in the encryption dictionary is greater than 1 in which case n is the value of
+ * Length divided by 8). Because we store the encryption key is as a PDF string object,
+ * we can just use the length of the string data, we calculated the length as part of
+ * creating the key.
+ */
+ memcpy(Buffer, ctx->encryption.EKey->data, ctx->encryption.EKey->length);
+ idx = ctx->encryption.EKey->length;
+ Buffer[idx] = object_num & 0xff;
+ Buffer[++idx] = (object_num & 0xff00) >> 8;
+ Buffer[++idx] = (object_num & 0xff0000) >> 16;
+ Buffer[++idx] = generation_num & 0xff;
+ Buffer[++idx] = (generation_num & 0xff00) >> 8;
+
+ md5_length = ctx->encryption.EKey->length + 5;
+
+ /* If using the AES algorithm, extend the encryption key an additional 4 bytes
+ * by adding the value "sAlT" which corresponds to the hexadecimal 0x73416c54
+ * (This addition is done for backward compatibility and is not intended to
+ * provide addtional security).
+ */
+ if (ctx->encryption.StmF == CRYPT_AESV2 || ctx->encryption.StmF == CRYPT_AESV3){
+ memcpy(&Buffer[++idx], sAlTString, 4);
+ md5_length += 4;
+ }
+
+ /* Step 3
+ * Initialise the MD5 function and pass the result of step 2 as input to this function
+ */
+ gs_md5_init(&md5);
+ gs_md5_append(&md5, (gs_md5_byte_t *)Buffer, md5_length);
+ gs_md5_finish(&md5, (gs_md5_byte_t *)Buffer);
+
+ /* Step 4
+ * Use the first n+5 bytes, up to a maximum of 16, of the output from the MD5
+ * hash as the key for the RC4 or AES symmetric key algorithms, along with the
+ * string or stream data to be encrypted.
+ */
+ ELength = ctx->encryption.EKey->length + 5;
+ if (ELength > 16)
+ ELength = 16;
+
+ code = pdfi_object_alloc(ctx, PDF_STRING, (uint64_t)ELength, (pdf_obj **)Key);
+ if (code >= 0)
+ memcpy((*Key)->data, Buffer, ELength);
+ /* pdfi_object_alloc() creates objects with a refrence count of 0 */
+ pdfi_countup(*Key);
+
+ gs_free_object(ctx->memory, Buffer, "");
+ } else {
+ /* Revision 5 & 6 don't use the object number and generation, just return the pre-calculated key */
+ *Key = ctx->encryption.EKey;
+ pdfi_countup(*Key);
+ }
+ return code;
+}
+
+int pdfi_decrypt_string(pdf_context *ctx, pdf_string *string)
+{
+ int code = 0;
+ pdf_c_stream *stream = NULL, *crypt_stream = NULL;
+ pdf_string *EKey = NULL;
+ char *Buffer = NULL;
+
+ if (!is_compressed_object(ctx, string->indirect_num, string->indirect_gen)) {
+ Buffer = (char *)gs_alloc_bytes(ctx->memory, string->length, "pdfi_decrypt_string");
+ if (Buffer == NULL)
+ return_error(gs_error_VMerror);
+
+ code = pdfi_compute_objkey(ctx, (pdf_obj *)string, &EKey);
+ if (code < 0)
+ goto error;
+
+ code = pdfi_open_memory_stream_from_memory(ctx, string->length, (byte *)string->data, &stream, true);
+ if (code < 0)
+ goto error;
+
+ switch(ctx->encryption.StrF) {
+ /* There are only two possible filters, RC4 or AES, we take care
+ * of the number of bits in the key by using ctx->Length.
+ */
+ case CRYPT_IDENTITY:
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ code = 0;
+ goto error;
+ break;
+ case CRYPT_V1:
+ case CRYPT_V2:
+ code = pdfi_apply_Arc4_filter(ctx, EKey, stream, &crypt_stream);
+ break;
+ case CRYPT_AESV2:
+ case CRYPT_AESV3:
+ code = pdfi_apply_AES_filter(ctx, EKey, 1, stream, &crypt_stream);
+ break;
+ default:
+ code = gs_error_rangecheck;
+ }
+ if (code < 0) {
+ pdfi_close_memory_stream(ctx, NULL, stream);
+ goto error;
+ }
+
+ sfread(Buffer, 1, string->length, crypt_stream->s);
+
+ pdfi_close_file(ctx, crypt_stream);
+ pdfi_close_memory_stream(ctx, NULL, stream);
+
+ memcpy(string->data, Buffer, string->length);
+ }
+
+error:
+ gs_free_object(ctx->memory, Buffer, "pdfi_decrypt_string");
+ pdfi_countdown(EKey);
+ return code;
+}
+
+/* Read the Encrypt dictionary entries and store the relevant ones
+ * in the PDF context for easy access. Return < 0 = error, 0 = encrypted
+ * and read the encryption details, 1 = not encrypted.
+ */
+static int pdfi_read_Encrypt_dict(pdf_context *ctx, int *KeyLen)
+{
+ int code = 0;
+ pdf_dict *CF_dict = NULL, *StdCF_dict = NULL;
+ pdf_dict *d = NULL;
+ pdf_obj *o = NULL;
+ pdf_string *s = NULL;
+ int64_t i64;
+ double f;
+
+ if (ctx->args.pdfdebug)
+ dmprintf(ctx->memory, "%% Checking for Encrypt dictionary\n");
+
+ code = pdfi_dict_get(ctx, ctx->Trailer, "Encrypt", (pdf_obj **)&d);
+
+ /* Undefined is acceptable here, it just means the PDF file is not ostensibly encrypted */
+ /* NB pdfi_process_pdf_file() always checks for the Encrypt dictionary before we
+ * get here, so there shouldn't be a problem.....
+ */
+ if (code == gs_error_undefined)
+ return 1;
+ else
+ if (code < 0)
+ goto done;
+
+ code = pdfi_dict_get_type(ctx, d, "Filter", PDF_NAME, &o);
+ if (code < 0)
+ goto done;
+
+ if (!pdfi_name_is((pdf_name *)o, "Standard")) {
+ char *Str = NULL;
+
+ Str = (char *)gs_alloc_bytes(ctx->memory, ((pdf_name *)o)->length + 1, "temp string for warning");
+ if (Str == NULL) {
+ code = gs_note_error(gs_error_VMerror);
+ goto done;
+ }
+ memset(Str, 0x00, ((pdf_name *)o)->length + 1);
+ memcpy(Str, ((pdf_name *)o)->data, ((pdf_name *)o)->length);
+ emprintf1(ctx->memory, "\n **** Warning: This file uses an unknown security handler %s\n", Str);
+ gs_free_object(ctx->memory, Str, "temp string for warning");
+ code = gs_note_error(gs_error_typecheck);
+ goto done;
+ }
+ pdfi_countdown(o);
+ o = NULL;
+
+ code = pdfi_dict_knownget_number(ctx, d, "Length", &f);
+ if (code < 0)
+ goto done;
+
+ if (code > 0)
+ *KeyLen = (int)f;
+ else
+ *KeyLen = 0;
+
+ code = pdfi_dict_get_int(ctx, d, "V", &i64);
+ if (code < 0)
+ goto done;
+
+ if (i64 < 1 || i64 > 5) {
+ code = gs_error_rangecheck;
+ goto done;
+ }
+
+ ctx->encryption.V = (int)i64;
+
+ code = pdfi_dict_get_int(ctx, d, "R", &i64);
+ if (code < 0)
+ goto done;
+ ctx->encryption.R = (int)i64;
+
+ code = pdfi_dict_get_int(ctx, d, "P", &i64);
+ if (code < 0)
+ goto done;
+ ctx->encryption.P = (int)i64;
+
+ code = pdfi_dict_get_type(ctx, d, "O", PDF_STRING, (pdf_obj **)&s);
+ if (code < 0)
+ goto done;
+
+ if (ctx->encryption.R < 5) {
+ if (s->length < 32) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto done;
+ }
+ memcpy(ctx->encryption.O, s->data, 32);
+ } else {
+ if (s->length < 48) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto done;
+ }
+ memcpy(ctx->encryption.O, s->data, 48);
+ }
+ pdfi_countdown(s);
+ s = NULL;
+
+ code = pdfi_dict_get_type(ctx, d, "U", PDF_STRING, (pdf_obj **)&s);
+ if (code < 0)
+ goto done;
+
+ if (ctx->encryption.R < 5) {
+ if (s->length < 32) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto done;
+ }
+ memcpy(ctx->encryption.U, s->data, 32);
+ } else {
+ if (s->length < 48) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto done;
+ }
+ memcpy(ctx->encryption.U, s->data, 48);
+ }
+ pdfi_countdown(s);
+ s = NULL;
+
+ code = pdfi_dict_knownget_type(ctx, d, "EncryptMetadata", PDF_BOOL, &o);
+ if (code < 0)
+ goto done;
+ if (code > 0) {
+ ctx->encryption.EncryptMetadata = ((pdf_bool *)o)->value;
+ pdfi_countdown(o);
+ }
+ else
+ ctx->encryption.EncryptMetadata = true;
+
+ if (ctx->encryption.R > 3) {
+ /* Check the Encrypt dictionary has default values for Stmf and StrF
+ * and that they have the names /StdCF. We don't support anything else.
+ */
+ code = pdfi_dict_get_type(ctx, d, "StmF", PDF_NAME, &o);
+ if (code < 0)
+ goto done;
+ if (!pdfi_name_is((pdf_name *)o, "StdCF")) {
+ if (pdfi_name_is((pdf_name *)o, "Identity")) {
+ ctx->encryption.StmF = CRYPT_IDENTITY;
+ } else {
+ code = gs_note_error(gs_error_undefined);
+ goto done;
+ }
+ }
+ pdfi_countdown(o);
+ o = NULL;
+
+ code = pdfi_dict_knownget_type(ctx, d, "StrF", PDF_NAME, &o);
+ if (code < 0)
+ goto done;
+ if (!pdfi_name_is((pdf_name *)o, "StdCF")) {
+ if (pdfi_name_is((pdf_name *)o, "Identity")) {
+ ctx->encryption.StrF = CRYPT_IDENTITY;
+ } else {
+ code = gs_note_error(gs_error_undefined);
+ goto done;
+ }
+ }
+ pdfi_countdown(o);
+ o = NULL;
+
+ /* Validated StmF and StrF, now check the Encrypt dictionary for the definition of
+ * the Crypt Filter dictionary and ensure it has a /StdCF dictionary.
+ */
+ code = pdfi_dict_get_type(ctx, d, "CF", PDF_DICT, (pdf_obj **)&CF_dict);
+ if (code < 0)
+ goto done;
+
+ code = pdfi_dict_get_type(ctx, CF_dict, "StdCF", PDF_DICT, (pdf_obj **)&StdCF_dict);
+ if (code < 0)
+ goto done;
+
+ code = pdfi_dict_get_type(ctx, StdCF_dict, "CFM", PDF_NAME, &o);
+ if (code < 0)
+ goto done;
+
+ if (pdfi_name_is((pdf_name *)o, "V2")) {
+ if (ctx->encryption.StmF == CRYPT_NONE)
+ ctx->encryption.StmF = CRYPT_V2;
+ if (ctx->encryption.StrF == CRYPT_NONE)
+ ctx->encryption.StrF = CRYPT_V2;
+ } else {
+ if (pdfi_name_is((pdf_name *)o, "AESV2")) {
+ if (ctx->encryption.StmF == CRYPT_NONE)
+ ctx->encryption.StmF = CRYPT_AESV2;
+ if (ctx->encryption.StrF == CRYPT_NONE)
+ ctx->encryption.StrF = CRYPT_AESV2;
+ } else {
+ if (pdfi_name_is((pdf_name *)o, "AESV3")) {
+ if (ctx->encryption.StmF == CRYPT_NONE)
+ ctx->encryption.StmF = CRYPT_AESV3;
+ if (ctx->encryption.StrF == CRYPT_NONE)
+ ctx->encryption.StrF = CRYPT_AESV3;
+ } else {
+ emprintf(ctx->memory, "\n **** Error: Unknown default encryption method in crypt filter.\n");
+ code = gs_error_rangecheck;
+ goto done;
+ }
+ }
+ }
+ pdfi_countdown(o);
+ o = NULL;
+
+ if (ctx->encryption.R > 4) {
+ code = pdfi_dict_get_type(ctx, d, "OE", PDF_STRING, (pdf_obj **)&s);
+ if (code < 0)
+ goto done;
+
+ if (s->length != 32) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto done;
+ }
+ memcpy(ctx->encryption.OE, s->data, 32);
+ pdfi_countdown(s);
+ s = NULL;
+
+ code = pdfi_dict_get_type(ctx, d, "UE", PDF_STRING, (pdf_obj **)&s);
+ if (code < 0)
+ goto done;
+
+ if (s->length != 32) {
+ code = gs_note_error(gs_error_rangecheck);
+ goto done;
+ }
+ memcpy(ctx->encryption.UE, s->data, 32);
+ pdfi_countdown(s);
+ s = NULL;
+ }
+ }
+
+done:
+ pdfi_countdown(StdCF_dict);
+ pdfi_countdown(CF_dict);
+ pdfi_countdown(s);
+ pdfi_countdown(o);
+ pdfi_countdown(d);
+ return code;
+}
+
+static int check_password_preR5(pdf_context *ctx, char *Password, int PasswordLen, int KeyLen, int Revision)
+{
+ int code;
+
+ if (PasswordLen != 0) {
+ code = check_user_password_preR5(ctx, Password, PasswordLen, KeyLen, Revision);
+ if (code >= 0)
+ return 0;
+
+ code = check_owner_password_preR5(ctx, Password, PasswordLen, KeyLen, Revision);
+ if (code >= 0)
+ return 0;
+ }
+ code = check_user_password_preR5(ctx, (char *)"", 0, KeyLen, Revision);
+ if (code >= 0)
+ return 0;
+
+ return check_owner_password_preR5(ctx, (char *)"", 0, KeyLen, Revision);
+}
+
+static int check_password_R5(pdf_context *ctx, char *Password, int PasswordLen, int KeyLen)
+{
+ int code;
+
+ if (PasswordLen != 0) {
+ code = check_user_password_R5(ctx, Password, PasswordLen, KeyLen);
+ if (code >= 0)
+ return 0;
+
+ code = check_owner_password_R5(ctx, Password, PasswordLen, KeyLen);
+ if (code >= 0)
+ return 0;
+
+ /* If the supplied Password fails as the user *and* owner password, maybe its in
+ * the locale, not UTF-8, try converting to UTF-8
+ */
+ if (code < 0) {
+ pdf_string *P = NULL, *P_UTF8 = NULL;
+
+ code = pdfi_object_alloc(ctx, PDF_STRING, strlen(ctx->encryption.Password), (pdf_obj **)&P);
+ if (code < 0) {
+ return code;
+ }
+ memcpy(P->data, Password, PasswordLen);
+ pdfi_countup(P);
+ code = locale_to_utf8(ctx, P, &P_UTF8);
+ if (code < 0) {
+ pdfi_countdown(P);
+ return code;
+ }
+ code = check_user_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen);
+ if (code >= 0) {
+ pdfi_countdown(P);
+ pdfi_countdown(P_UTF8);
+ return code;
+ }
+
+ code = check_owner_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen);
+ pdfi_countdown(P);
+ pdfi_countdown(P_UTF8);
+ if (code >= 0)
+ return code;
+ }
+ }
+ code = check_user_password_R5(ctx, (char *)"", 0, KeyLen);
+ if (code >= 0)
+ return 0;
+
+ return check_owner_password_R5(ctx, (char *)"", 0, KeyLen);
+}
+
+static int check_password_R6(pdf_context *ctx, char *Password, int PasswordLen, int KeyLen)
+{
+ int code;
+
+ if (PasswordLen != 0) {
+ code = check_user_password_R6(ctx, Password, PasswordLen, KeyLen);
+ if (code >= 0)
+ return 0;
+
+ code = check_owner_password_R6(ctx, Password, PasswordLen, KeyLen);
+ if (code >= 0)
+ return 0;
+ /* If the supplied Password fails as the user *and* owner password, maybe its in
+ * the locale, not UTF-8, try converting to UTF-8
+ */
+ if (code < 0) {
+ pdf_string *P = NULL, *P_UTF8 = NULL;
+
+ code = pdfi_object_alloc(ctx, PDF_STRING, strlen(ctx->encryption.Password), (pdf_obj **)&P);
+ if (code < 0)
+ return code;
+ memcpy(P->data, Password, PasswordLen);
+ pdfi_countup(P);
+ code = locale_to_utf8(ctx, P, &P_UTF8);
+ if (code < 0) {
+ pdfi_countdown(P);
+ return code;
+ }
+ code = check_user_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen);
+ if (code >= 0) {
+ pdfi_countdown(P);
+ pdfi_countdown(P_UTF8);
+ return code;
+ }
+
+ code = check_owner_password_R5(ctx, (char *)P_UTF8->data, P_UTF8->length, KeyLen);
+ pdfi_countdown(P);
+ pdfi_countdown(P_UTF8);
+ if (code >= 0)
+ return code;
+ }
+ }
+ code = check_user_password_R6(ctx, (char *)"", 0, KeyLen);
+ if (code >= 0)
+ return 0;
+
+ return check_owner_password_R6(ctx, (char *)"", 0, KeyLen);
+}
+
+/* Read the Encrypt dictionary entries and store the relevant ones
+ * in the PDF context for easy access. Check whether the file is
+ * readable without a password and if not, check to see if we've been
+ * supplied a password. If we have try the password as the user password
+ * and if that fails as the owner password. Store the calculated decryption key
+ * for later use decrypting objects.
+ */
+int pdfi_initialise_Decryption(pdf_context *ctx)
+{
+ int code = 0, KeyLen = 0;
+
+ code = pdfi_read_Encrypt_dict(ctx, &KeyLen);
+ if (code > 0)
+ return 0;
+ if (code < 0)
+ return code;
+
+ switch(ctx->encryption.R) {
+ case 2:
+ /* Set up the defaults if not already set */
+ /* Revision 2 is always 40-bit RC4 */
+ if (KeyLen == 0)
+ KeyLen = 40;
+ if (ctx->encryption.StmF == CRYPT_NONE)
+ ctx->encryption.StmF = CRYPT_V1;
+ if (ctx->encryption.StrF == CRYPT_NONE)
+ ctx->encryption.StrF = CRYPT_V1;
+ code = check_password_preR5(ctx, ctx->encryption.Password, ctx->encryption.PasswordLen, KeyLen, 2);
+ break;
+ case 3:
+ /* Set up the defaults if not already set */
+ /* Revision 3 is always 128-bit RC4 */
+ if (KeyLen == 0)
+ KeyLen = 128;
+ if (ctx->encryption.StmF == CRYPT_NONE)
+ ctx->encryption.StmF = CRYPT_V2;
+ if (ctx->encryption.StrF == CRYPT_NONE)
+ ctx->encryption.StrF = CRYPT_V2;
+ code = check_password_preR5(ctx, ctx->encryption.Password, ctx->encryption.PasswordLen, KeyLen, 3);
+ break;
+ case 4:
+ /* Revision 4 is either AES or RC4, but its always 128-bits */
+ if (KeyLen == 0)
+ KeyLen = 128;
+ /* We can't set the encryption filter, so we have to hope the PDF file did */
+ code = check_password_preR5(ctx, ctx->encryption.Password, ctx->encryption.PasswordLen, KeyLen, 4);
+ break;
+ case 5:
+ /* Set up the defaults if not already set */
+ if (KeyLen == 0)
+ KeyLen = 256;
+ if (ctx->encryption.StmF == CRYPT_NONE)
+ ctx->encryption.StmF = CRYPT_AESV2;
+ if (ctx->encryption.StrF == CRYPT_NONE)
+ ctx->encryption.StrF = CRYPT_AESV2;
+ code = check_password_R5(ctx, ctx->encryption.Password, ctx->encryption.PasswordLen, KeyLen);
+ break;
+ case 6:
+ /* Set up the defaults if not already set */
+ /* Revision 6 is always 256-bit AES */
+ if (KeyLen == 0)
+ KeyLen = 256;
+ if (ctx->encryption.StmF == CRYPT_NONE)
+ ctx->encryption.StmF = CRYPT_AESV3;
+ if (ctx->encryption.StrF == CRYPT_NONE)
+ ctx->encryption.StrF = CRYPT_AESV3;
+ code = check_password_R6(ctx, ctx->encryption.Password, ctx->encryption.PasswordLen, KeyLen);
+ break;
+ default:
+ emprintf1(ctx->memory, "\n **** Warning: This file uses an unknown standard security handler revision: %d\n", ctx->encryption.R);
+ code = gs_error_rangecheck;
+ goto done;
+ }
+ if (code < 0) {
+ if(ctx->encryption.Password) {
+ emprintf(ctx->memory, "\n **** Error: Password did not work.\n");
+ emprintf(ctx->memory, " Cannot decrypt PDF file.\n");
+ } else
+ emprintf(ctx->memory, "\n **** This file requires a password for access.\n");
+ } else
+ ctx->encryption.is_encrypted = true;
+
+done:
+ return code;
+}