From 019f0e800372a7459b3e81f4b2333ef619889def Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Sun, 21 Dec 2025 19:03:34 -0500 Subject: [PATCH] Support Encrypt dictionaries as well as indirect references (Issue #139) --- CHANGES.md | 2 ++ pdfio-crypto.c | 36 +++++++++++++++--------------------- pdfio-file.c | 41 +++++++++++++++++++++++++++++++++-------- pdfio-private.h | 3 ++- 4 files changed, 52 insertions(+), 30 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5460016..4686944 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,8 @@ v1.6.1 - YYYY-MM-DD - Added missing input checking to `pdfioFileCreateFontObjFromBase` function. - Updated support for UTF-16 strings (Issue #141) - Updated Xcode project to use installed PNG library. +- Fixed decryption of PDF files using an Encrypt dictionary instead of an + indirect reference (Issue #139) - Fixed character range checking in a TTF support function. - Fixed some clang warnings. diff --git a/pdfio-crypto.c b/pdfio-crypto.c index 3badeff..420f4a4 100644 --- a/pdfio-crypto.c +++ b/pdfio-crypto.c @@ -214,8 +214,9 @@ _pdfioCryptoLock( pdfioObjClose(pdf->encrypt_obj); - pdf->encryption = encryption; - pdf->permissions = permissions; + pdf->encrypt_dict = dict; + pdf->encryption = encryption; + pdf->permissions = permissions; return (true); } @@ -570,7 +571,6 @@ _pdfioCryptoUnlock( { int tries; // Number of tries const char *password = NULL; // Password to try - pdfio_dict_t *encrypt_dict; // Encrypt objection dictionary int version, // Version value revision, // Revision value length; // Key length value @@ -590,20 +590,14 @@ _pdfioCryptoUnlock( _pdfio_value_t *value; // Encrypt dictionary value, if any - // See if we support the type of encryption specified by the Encrypt object + // See if we support the type of encryption specified by the Encrypt // dictionary... - if ((encrypt_dict = pdfioObjGetDict(pdf->encrypt_obj)) == NULL) - { - _pdfioFileError(pdf, "Unable to get encryption dictionary."); - return (false); - } + handler = pdfioDictGetName(pdf->encrypt_dict, "Filter"); + version = (int)pdfioDictGetNumber(pdf->encrypt_dict, "V"); + revision = (int)pdfioDictGetNumber(pdf->encrypt_dict, "R"); + length = (int)pdfioDictGetNumber(pdf->encrypt_dict, "Length"); - handler = pdfioDictGetName(encrypt_dict, "Filter"); - version = (int)pdfioDictGetNumber(encrypt_dict, "V"); - revision = (int)pdfioDictGetNumber(encrypt_dict, "R"); - length = (int)pdfioDictGetNumber(encrypt_dict, "Length"); - - if ((value = _pdfioDictGetValue(encrypt_dict, "EncryptMetadata")) != NULL && value->type == PDFIO_VALTYPE_BOOLEAN) + if ((value = _pdfioDictGetValue(pdf->encrypt_dict, "EncryptMetadata")) != NULL && value->type == PDFIO_VALTYPE_BOOLEAN) pdf->encrypt_metadata = value->value.boolean; else pdf->encrypt_metadata = true; @@ -622,9 +616,9 @@ _pdfioCryptoUnlock( pdfio_dict_t *filter; // Crypt Filter const char *cfm; // Crypt filter method - stream_filter = pdfioDictGetName(encrypt_dict, "StmF"); - string_filter = pdfioDictGetName(encrypt_dict, "StrF"); - cf_dict = pdfioDictGetDict(encrypt_dict, "CF"); + stream_filter = pdfioDictGetName(pdf->encrypt_dict, "StmF"); + string_filter = pdfioDictGetName(pdf->encrypt_dict, "StrF"); + cf_dict = pdfioDictGetDict(pdf->encrypt_dict, "CF"); if (!cf_dict) { @@ -701,7 +695,7 @@ _pdfioCryptoUnlock( // Grab the remaining values we need to unlock the PDF... pdf->file_keylen = (size_t)(length / 8); - p = pdfioDictGetNumber(encrypt_dict, "P"); + p = pdfioDictGetNumber(pdf->encrypt_dict, "P"); PDFIO_DEBUG("_pdfioCryptoUnlock: P=%.0f\n", p); if (p < 0x7fffffff) // Handle integers > 2^31-1 pdf->permissions = (pdfio_permission_t)p; @@ -709,8 +703,8 @@ _pdfioCryptoUnlock( pdf->permissions = (pdfio_permission_t)(p - 4294967296.0); PDFIO_DEBUG("_pdfioCryptoUnlock: permissions=%d\n", pdf->permissions); - owner_key = pdfioDictGetBinary(encrypt_dict, "O", &owner_keylen); - user_key = pdfioDictGetBinary(encrypt_dict, "U", &user_keylen); + owner_key = pdfioDictGetBinary(pdf->encrypt_dict, "O", &owner_keylen); + user_key = pdfioDictGetBinary(pdf->encrypt_dict, "U", &user_keylen); if (!owner_key) { diff --git a/pdfio-file.c b/pdfio-file.c index 4570e06..e30c63d 100644 --- a/pdfio-file.c +++ b/pdfio-file.c @@ -2283,12 +2283,18 @@ load_xref( { // Save the trailer dictionary and grab the root (catalog) and info // objects... + pdfio_obj_t *encrypt_obj; // Encryption object + pdf->trailer_dict = trailer.value.dict; - pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt"); pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID"); + if ((encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL) + pdf->encrypt_dict = pdfioObjGetDict(encrypt_obj); + else + pdf->encrypt_dict = pdfioDictGetDict(pdf->trailer_dict, "Encrypt"); + // If the trailer contains an Encrypt key, try unlocking the file... - if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data)) + if (pdf->encrypt_dict && !_pdfioCryptoUnlock(pdf, password_cb, password_data)) return (false); } @@ -2434,12 +2440,18 @@ load_xref( { // Save the trailer dictionary and grab the root (catalog) and info // objects... + pdfio_obj_t *encrypt_obj; // Encryption object + pdf->trailer_dict = trailer.value.dict; - pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt"); pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID"); + if ((encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL) + pdf->encrypt_dict = pdfioObjGetDict(encrypt_obj); + else + pdf->encrypt_dict = pdfioDictGetDict(pdf->trailer_dict, "Encrypt"); + // If the trailer contains an Encrypt key, try unlocking the file... - if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data)) + if (pdf->encrypt_dict && !_pdfioCryptoUnlock(pdf, password_cb, password_data)) return (false); } } @@ -2529,7 +2541,7 @@ repair_xref( pdf->root_obj = NULL; pdf->info_obj = NULL; pdf->pages_obj = NULL; - pdf->encrypt_obj = NULL; + pdf->encrypt_dict = NULL; // Read from the beginning of the file, looking for objects... if ((line_offset = _pdfioFileSeek(pdf, 0, SEEK_SET)) < 0) @@ -2603,10 +2615,17 @@ repair_xref( if (!strcmp(type, "XRef") && !pdf->trailer_dict) { // Save the trailer dictionary... + pdfio_obj_t *encrypt_obj; + // Encryption object + PDFIO_DEBUG("repair_xref: XRef stream...\n"); pdf->trailer_dict = pdfioObjGetDict(obj); - pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt"); pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID"); + + if ((encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL) + pdf->encrypt_dict = pdfioObjGetDict(encrypt_obj); + else + pdf->encrypt_dict = pdfioDictGetDict(pdf->trailer_dict, "Encrypt"); } } else if (type && !strcmp(line, "endobj")) @@ -2660,11 +2679,17 @@ repair_xref( { // Save the trailer dictionary and grab the root (catalog) and info // objects... + pdfio_obj_t *encrypt_obj; // Encryption object + PDFIO_DEBUG("repair_xref: Using this trailer dictionary.\n"); pdf->trailer_dict = trailer.value.dict; - pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt"); pdf->id_array = pdfioDictGetArray(pdf->trailer_dict, "ID"); + + if ((encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL) + pdf->encrypt_dict = pdfioObjGetDict(encrypt_obj); + else + pdf->encrypt_dict = pdfioDictGetDict(pdf->trailer_dict, "Encrypt"); } } @@ -2678,7 +2703,7 @@ repair_xref( pdf->trailer_dict = backup_trailer; // If the trailer contains an Encrypt key, try unlocking the file... - if (pdf->encrypt_obj && !_pdfioCryptoUnlock(pdf, password_cb, password_data)) + if (pdf->encrypt_dict && !_pdfioCryptoUnlock(pdf, password_cb, password_data)) return (false); // Load any stream objects... diff --git a/pdfio-private.h b/pdfio-private.h index 78753d9..7ce2e26 100644 --- a/pdfio-private.h +++ b/pdfio-private.h @@ -283,7 +283,8 @@ struct _pdfio_file_s // PDF file structure pdfio_obj_t *root_obj; // Root object/dictionary pdfio_obj_t *info_obj; // Information object pdfio_obj_t *pages_obj; // Root pages object - pdfio_obj_t *encrypt_obj; // De/Encryption object/dictionary + pdfio_obj_t *encrypt_obj; // Encryption object (not used for reading) + pdfio_dict_t *encrypt_dict; // De/Encryption dictionary pdfio_obj_t *cgats001_obj, // CGATS001 ICC profile object *cp1252_obj, // CP1252 font encoding object *unicode_obj; // Unicode font encoding object