Support Encrypt dictionaries as well as indirect references (Issue #139)

This commit is contained in:
Michael R Sweet
2025-12-21 19:03:34 -05:00
parent d5d089c560
commit 019f0e8003
4 changed files with 52 additions and 30 deletions

View File

@@ -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.

View File

@@ -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)
{

View File

@@ -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...

View File

@@ -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