From 4ca93bd34fabbd409ba5d863cea5cf4a356699b5 Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Wed, 23 Apr 2025 10:07:44 -0400 Subject: [PATCH] Add support for EncryptMetadata key in encryption dictionary. --- pdfio-crypto.c | 32 +++++++++++++++++++++++++++----- pdfio-file.c | 2 ++ pdfio-object.c | 2 +- pdfio-private.h | 1 + 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/pdfio-crypto.c b/pdfio-crypto.c index b9100b2..53859bb 100644 --- a/pdfio-crypto.c +++ b/pdfio-crypto.c @@ -98,7 +98,7 @@ static uint8_t pdf_passpad[32] = // Padding for passwords static void decrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]); static void encrypt_user_key(pdfio_encryption_t encryption, const uint8_t *file_key, uint8_t user_key[32]); -static void make_file_key(pdfio_encryption_t encryption, pdfio_permission_t permissions, const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *owner_key, uint8_t file_key[16]); +static void make_file_key(pdfio_encryption_t encryption, pdfio_permission_t permissions, const unsigned char *file_id, size_t file_idlen, const uint8_t *user_pad, const uint8_t *owner_key, bool encrypt_metadata, uint8_t file_key[16]); static void make_owner_key(pdfio_encryption_t encryption, const uint8_t *owner_pad, const uint8_t *user_pad, uint8_t owner_key[32]); static void make_user_key(const unsigned char *file_id, size_t file_idlen, uint8_t user_key[32]); static void pad_password(const char *password, uint8_t pad[32]); @@ -158,7 +158,7 @@ _pdfioCryptoLock( // Generate the encryption key file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_idlen); - make_file_key(encryption, permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->file_key); + make_file_key(encryption, permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->encrypt_metadata, pdf->file_key); pdf->file_keylen = 16; // Generate the user key... @@ -583,6 +583,7 @@ _pdfioCryptoUnlock( _pdfio_md5_t md5; // MD5 context uint8_t file_digest[16]; // MD5 digest of file ID and pad double p; // Permissions value as a double + _pdfio_value_t *value; // Encrypt dictionary value, if any // See if we support the type of encryption specified by the Encrypt object @@ -598,7 +599,12 @@ _pdfioCryptoUnlock( revision = (int)pdfioDictGetNumber(encrypt_dict, "R"); length = (int)pdfioDictGetNumber(encrypt_dict, "Length"); - PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d\n", (void *)handler, handler ? handler : "(null)", version, revision, length); + if ((value = _pdfioDictGetValue(encrypt_dict, "EncryptMetadata")) != NULL && value->type == PDFIO_VALTYPE_BOOLEAN) + pdf->encrypt_metadata = value->value.boolean; + else + pdf->encrypt_metadata = true; + + PDFIO_DEBUG("_pdfioCryptoUnlock: handler=%p(%s), version=%d, revision=%d, length=%d, encrypt_metadata=%s\n", (void *)handler, handler ? handler : "(null)", version, revision, length, pdf->encrypt_metadata ? "true" : "false"); if (!handler || strcmp(handler, "Standard")) { @@ -768,7 +774,7 @@ _pdfioCryptoUnlock( make_owner_key(pdf->encryption, pad, pdf->owner_key, user_pad); PDFIO_DEBUG("_pdfioCryptoUnlock: Upad=%02X%02X%02X%02X...%02X%02X%02X%02X\n", user_pad[0], user_pad[1], user_pad[2], user_pad[3], user_pad[28], user_pad[29], user_pad[30], user_pad[31]); - make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, user_pad, pdf->owner_key, file_key); + make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, user_pad, pdf->owner_key, pdf->encrypt_metadata, file_key); PDFIO_DEBUG("_pdfioCryptoUnlock: Fown=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]); make_user_key(file_id, file_idlen, own_user_key); @@ -786,7 +792,7 @@ _pdfioCryptoUnlock( } // Not the owner password, try the user password... - make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, pad, pdf->owner_key, file_key); + make_file_key(pdf->encryption, pdf->permissions, file_id, file_idlen, pad, pdf->owner_key, pdf->encrypt_metadata, file_key); PDFIO_DEBUG("_pdfioCryptoUnlock: Fuse=%02X%02X%02X%02X...%02X%02X%02X%02X\n", file_key[0], file_key[1], file_key[2], file_key[3], file_key[12], file_key[13], file_key[14], file_key[15]); make_user_key(file_id, file_idlen, own_user_key); @@ -918,6 +924,8 @@ make_file_key( size_t file_idlen, // I - Length of file ID const uint8_t *user_pad, // I - Padded user password const uint8_t *owner_key, // I - Owner key + bool encrypt_metadata, + // I - Encrypt metadata? uint8_t file_key[16]) // O - Encryption key { size_t i; // Looping var @@ -931,13 +939,25 @@ make_file_key( perm_bytes[2] = (uint8_t)(permissions >> 16); perm_bytes[3] = (uint8_t)(permissions >> 24); + PDFIO_DEBUG("make_file_key: user_pad[32]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", user_pad[0], user_pad[1], user_pad[2], user_pad[3], user_pad[28], user_pad[29], user_pad[30], user_pad[31]); + PDFIO_DEBUG("make_file_key: owner_key[32]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", owner_key[0], owner_key[1], owner_key[2], owner_key[3], owner_key[28], owner_key[29], owner_key[30], owner_key[31]); + PDFIO_DEBUG("make_file_key: permissions(%d)=<%02X%02X%02X%02X>\n", permissions, perm_bytes[0], perm_bytes[1], perm_bytes[2], perm_bytes[3]); + _pdfioCryptoMD5Init(&md5); _pdfioCryptoMD5Append(&md5, user_pad, 32); _pdfioCryptoMD5Append(&md5, owner_key, 32); _pdfioCryptoMD5Append(&md5, perm_bytes, 4); _pdfioCryptoMD5Append(&md5, file_id, file_idlen); + if (!encrypt_metadata) + { + uint8_t meta_bytes[4] = { 0xff, 0xff, 0xff, 0xff }; + // Metadata bytes + _pdfioCryptoMD5Append(&md5, meta_bytes, 4); + } _pdfioCryptoMD5Finish(&md5, digest); + PDFIO_DEBUG("make_file_key: first md5=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", digest[0], digest[1], digest[2], digest[3], digest[12], digest[13], digest[14], digest[15]); + if (encryption != PDFIO_ENCRYPTION_RC4_40) { // MD5 the result 50 times.. @@ -949,6 +969,8 @@ make_file_key( } } + PDFIO_DEBUG("make_file_key: file_key[16]=<%02X%02X%02X%02X...%02X%02X%02X%02X>\n", digest[0], digest[1], digest[2], digest[3], digest[12], digest[13], digest[14], digest[15]); + memcpy(file_key, digest, 16); } diff --git a/pdfio-file.c b/pdfio-file.c index 6cd2b78..de2a379 100644 --- a/pdfio-file.c +++ b/pdfio-file.c @@ -1262,6 +1262,8 @@ pdfioFileSetPermissions( if (encryption == PDFIO_ENCRYPTION_NONE) return (true); + pdf->encrypt_metadata = true; + return (_pdfioCryptoLock(pdf, permissions, encryption, owner_password, user_password)); } diff --git a/pdfio-object.c b/pdfio-object.c index 2ad1e0f..0598abc 100644 --- a/pdfio-object.c +++ b/pdfio-object.c @@ -507,7 +507,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object } // Decrypt as needed... - if (obj->pdf->encryption) + if (obj->pdf->encryption && obj->pdf->encrypt_metadata) { PDFIO_DEBUG("_pdfioObjLoad: Decrypting value...\n"); diff --git a/pdfio-private.h b/pdfio-private.h index bca58e0..a4b6998 100644 --- a/pdfio-private.h +++ b/pdfio-private.h @@ -271,6 +271,7 @@ struct _pdfio_file_s // PDF file structure *cp1252_obj, // CP1252 font encoding object *unicode_obj; // Unicode font encoding object pdfio_array_t *id_array; // ID array + bool encrypt_metadata; // Encrypt metadata? // Allocated data elements size_t num_arrays, // Number of arrays