Do some reorganization and start the implementation of decryption.

This commit is contained in:
Michael R Sweet 2021-10-24 10:59:25 -04:00
parent b7ecaeee07
commit 234c3a7381
No known key found for this signature in database
GPG Key ID: 999559A027815955
6 changed files with 465 additions and 256 deletions

17
FAQ.md
View File

@ -1,17 +0,0 @@
Frequently Asked Questions
==========================
Why Don't You Support Writing a PDF File with Encryption?
---------------------------------------------------------
PDF encryption offers very little protection:
- PDF encryption keys are reused and derived from the user password (padded
with a standard base string) and the object numbers in the file.
- RC4 encryption (40- and 128-bit) was broken years ago.
- AES encryption (128- and 256-bit) is better, but PDF uses Cipher Block
Chaining (CBC) which enables attacks that allow the original encryption key
to be recovered.
In addition, PDF usage controls (no print, no copy, etc.) are tied to this
encryption, making them trivial to bypass.

View File

@ -23,6 +23,228 @@
#endif // __has_include
//
// Local globals...
//
static uint8_t pdf_passpad[32] = // Padding for passwords
{
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
};
//
// '_pdfioCryptoLock()' - Lock a PDF file by generating the encryption object and keys.
//
bool // O - `true` on success, `false` otherwise
_pdfioCryptoLock(
pdfio_file_t *pdf, // I - PDF file
pdfio_permission_t permissions, // I - Use permissions
pdfio_encryption_t encryption, // I - Type of encryption to use
const char *owner_password, // I - Owner password, if any
const char *user_password) // I - User password, if any
{
pdfio_dict_t *dict; // Encryption dictionary
size_t i, j; // Looping vars
_pdfio_md5_t md5; // MD5 context
uint8_t digest[16]; // 128-bit MD5 digest
_pdfio_rc4_t rc4; // RC4 encryption context
size_t len; // Length of password
uint8_t owner_pad[32], // Padded owner password
user_pad[32], // Padded user password
perm_bytes[4], // Permissions bytes
*file_id; // File ID bytes
size_t file_id_len; // Length of file ID
pdfio_dict_t *cf_dict, // CF dictionary
*filter_dict; // CryptFilter dictionary
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to create encryption dictionary.");
return (false);
}
pdfioDictSetName(dict, "Filter", "Standard");
switch (encryption)
{
case PDFIO_ENCRYPTION_RC4_128 :
case PDFIO_ENCRYPTION_AES_128 :
// Create the 128-bit encryption keys...
if (user_password)
{
// Use the specified user password
if ((len = strlen(user_password)) > sizeof(user_pad))
len = sizeof(user_pad);
}
else
{
// No user password
len = 0;
}
if (len > 0)
memcpy(user_pad, user_password, len);
if (len < sizeof(user_pad))
memcpy(user_pad + len, pdf_passpad, sizeof(user_pad) - len);
if (owner_password)
{
// Use the specified owner password...
if ((len = strlen(owner_password)) > sizeof(owner_pad))
len = sizeof(owner_pad);
}
else if (user_password && *user_password)
{
// Generate a random owner password...
_pdfioCryptoMakeRandom(owner_pad, sizeof(owner_pad));
len = sizeof(owner_pad);
}
else
{
// No owner password
len = 0;
}
if (len > 0)
memcpy(owner_pad, owner_password, len);
if (len < sizeof(owner_pad))
memcpy(owner_pad + len, pdf_passpad, sizeof(owner_pad) - len);
// Compute the owner key...
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, owner_pad, 32);
_pdfioCryptoMD5Finish(&md5, digest);
for (i = 0; i < 50; i ++)
{
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, digest, 16);
_pdfioCryptoMD5Finish(&md5, digest);
}
// Copy and encrypt the padded user password...
memcpy(pdf->owner_key, user_pad, sizeof(pdf->owner_key));
for (i = 0; i < 20; i ++)
{
uint8_t encrypt_key[16]; // RC4 encryption key
// XOR each byte in the digest with the loop counter to make a key...
for (j = 0; j < sizeof(encrypt_key); j ++)
encrypt_key[j] = (uint8_t)(digest[j] ^ i);
_pdfioCryptoRC4Init(&rc4, encrypt_key, sizeof(encrypt_key));
_pdfioCryptoRC4Crypt(&rc4, pdf->owner_key, pdf->owner_key, sizeof(pdf->owner_key));
}
pdf->owner_keylen = 32;
// Generate the encryption key
perm_bytes[0] = (uint8_t)permissions;
perm_bytes[1] = (uint8_t)(permissions >> 8);
perm_bytes[2] = (uint8_t)(permissions >> 16);
perm_bytes[3] = (uint8_t)(permissions >> 24);
file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_id_len);
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, user_pad, 32);
_pdfioCryptoMD5Append(&md5, pdf->owner_key, 32);
_pdfioCryptoMD5Append(&md5, perm_bytes, 4);
_pdfioCryptoMD5Append(&md5, file_id, file_id_len);
_pdfioCryptoMD5Finish(&md5, digest);
// MD5 the result 50 times..
for (i = 0; i < 50; i ++)
{
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, digest, 16);
_pdfioCryptoMD5Finish(&md5, digest);
}
memcpy(pdf->encryption_key, digest, 16);
pdf->encryption_keylen = 16;
// Generate the user key...
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, pdf_passpad, 32);
_pdfioCryptoMD5Append(&md5, file_id, file_id_len);
_pdfioCryptoMD5Finish(&md5, pdf->user_key);
memset(pdf->user_key + 16, 0, 16);
// Encrypt the result 20 times...
for (i = 0; i < 20; i ++)
{
// XOR each byte in the key with the loop counter...
for (j = 0; j < 16; j ++)
digest[j] = (uint8_t)(pdf->encryption_key[j] ^ i);
_pdfioCryptoRC4Init(&rc4, digest, 16);
_pdfioCryptoRC4Crypt(&rc4, pdf->user_key, pdf->user_key, sizeof(pdf->user_key));
}
pdf->user_keylen = 32;
// Save everything in the dictionary...
pdfioDictSetNumber(dict, "Length", 128);
pdfioDictSetBinary(dict, "O", pdf->owner_key, sizeof(pdf->owner_key));
pdfioDictSetNumber(dict, "P", (int)permissions);
pdfioDictSetNumber(dict, "R", encryption == PDFIO_ENCRYPTION_RC4_128 ? 3 : 4);
pdfioDictSetNumber(dict, "V", encryption == PDFIO_ENCRYPTION_RC4_128 ? 2 : 4);
pdfioDictSetBinary(dict, "U", pdf->user_key, sizeof(pdf->user_key));
if (encryption == PDFIO_ENCRYPTION_AES_128)
{
if ((cf_dict = pdfioDictCreate(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to create Encryption CF dictionary.");
return (false);
}
if ((filter_dict = pdfioDictCreate(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to create Encryption CryptFilter dictionary.");
return (false);
}
pdfioDictSetName(filter_dict, "Type", "CryptFilter");
pdfioDictSetName(filter_dict, "CFM", encryption == PDFIO_ENCRYPTION_RC4_128 ? "V2" : "AESV2");
pdfioDictSetDict(cf_dict, "PDFio", filter_dict);
pdfioDictSetDict(dict, "CF", cf_dict);
pdfioDictSetName(dict, "StmF", "PDFio");
pdfioDictSetName(dict, "StrF", "PDFio");
pdfioDictSetBoolean(dict, "EncryptMetadata", true);
}
break;
case PDFIO_ENCRYPTION_AES_256 :
// TODO: Implement AES-256 (/V 6 /R 6)
default :
_pdfioFileError(pdf, "Encryption mode %d not supported for writing.", (int)encryption);
return (false);
}
if ((pdf->encrypt_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
{
_pdfioFileError(pdf, "Unable to create encryption object.");
return (false);
}
pdfioObjClose(pdf->encrypt_obj);
pdf->encryption = encryption;
pdf->permissions = permissions;
return (true);
}
//
// '_pdfioCryptoMakeRandom()' - Fill a buffer with good random numbers.
//
@ -323,3 +545,137 @@ _pdfio_crypto_cb_t // O - Encryption callback or `NULL` for none
}
}
}
//
// '_pdfioCryptoUnlock()' - Unlock an encrypted PDF.
//
bool // O - `true` on success, `false` otherwise
_pdfioCryptoUnlock(
pdfio_file_t *pdf, // I - PDF file
pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none
void *password_data) // I - Password callback data, if any
{
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
const char *handler, // Security handler name
*stream_filter, // Stream encryption filter
*string_filter; // String encryption filter
pdfio_dict_t *cf_dict; // CryptFilters dictionary
unsigned char *owner_key, // Owner key
*user_key, // User key
*file_id; // File ID value
size_t owner_keylen, // Length of owner key
user_keylen, // Length of user key
file_idlen; // Length of file ID
// See if we support the type of encryption specified by the Encrypt object
// dictionary...
if ((encrypt_dict = pdfioObjGetDict(pdf->encrypt_obj)) == NULL)
{
_pdfioFileError(pdf, "Unable to get encryption dictionary.");
return (false);
}
handler = pdfioDictGetName(encrypt_dict, "Filter");
version = pdfioDictGetNumber(encrypt_dict, "V");
revision = pdfioDictGetNumber(encrypt_dict, "R");
length = pdfioDictGetNumber(encrypt_dict, "Length");
stream_filter = pdfioDictGetName(encrypt_dict, "StmF");
string_filter = pdfioDictGetName(encrypt_dict, "StrF");
cf_dict = pdfioDictGetDict(encrypt_dict, "CF");
if (!handler || strcmp(handler, "Standard"))
{
_pdfioFileError(pdf, "Unsupported security handler '%s'.", handler ? handler : "(null)");
return (false);
}
if (version == 4 && revision == 4)
{
// Lookup crypt filter to see if we support it...
pdfio_dict_t *filter; // Crypt Filter
const char *cfm; // Crypt filter method
if ((filter = pdfioDictGetDict(cf_dict, stream_filter)) != NULL && (cfm = pdfioDictGetName(filter, "CFM")) != NULL)
{
if (!strcmp(cfm, "V2"))
{
pdf->encryption = PDFIO_ENCRYPTION_RC4_128;
if (length < 40 || length > 128)
length = 128;
}
if (!strcmp(cfm, "AESV2"))
{
pdf->encryption = PDFIO_ENCRYPTION_AES_128;
length = 128;
}
}
}
else if (version == 2)
{
if (revision == 2)
{
pdf->encryption = PDFIO_ENCRYPTION_RC4_40;
length = 40;
}
else if (revision == 3)
{
pdf->encryption = PDFIO_ENCRYPTION_RC4_128;
if (length < 40 || length > 128)
length = 128;
}
}
// TODO: Implement AES-256 - V6 R6
if (pdf->encryption == PDFIO_ENCRYPTION_NONE)
{
_pdfioFileError(pdf, "Unsupported encryption V%d R%d.", version, revision);
return (false);
}
// Grab the remaining values we need to unlock the PDF...
pdf->encryption_keylen = length / 8;
pdf->permissions = pdfioDictGetNumber(encrypt_dict, "P");
owner_key = pdfioDictGetBinary(encrypt_dict, "O", &owner_keylen);
user_key = pdfioDictGetBinary(encrypt_dict, "U", &user_keylen);
if (!owner_key || owner_keylen < 32 || owner_keylen > sizeof(pdf->owner_key))
{
_pdfioFileError(pdf, "Missing or bad owner key, unable to unlock file.");
return (false);
}
memcpy(pdf->owner_key, owner_key, owner_keylen);
pdf->owner_keylen = owner_keylen;
if (!user_key || user_keylen < 32 || user_keylen > sizeof(pdf->user_key))
{
_pdfioFileError(pdf, "Missing or bad user key, unable to unlock file.");
return (false);
}
memcpy(pdf->user_key, user_key, user_keylen);
pdf->user_keylen = user_keylen;
if ((file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_idlen)) == NULL || file_idlen < 16)
{
_pdfioFileError(pdf, "Missing or bad file ID, unable to unlock file.");
return (false);
}
// Now try to unlock the PDF...
for (tries = 0; tries < 4; tries ++)
{
//
}
return (false);
}

View File

@ -26,7 +26,7 @@ static int compare_objmaps(_pdfio_objmap_t *a, _pdfio_objmap_t *b);
static int compare_objs(pdfio_obj_t **a, pdfio_obj_t **b);
static bool load_obj_stream(pdfio_obj_t *obj);
static bool load_pages(pdfio_file_t *pdf, pdfio_obj_t *obj);
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset);
static bool load_xref(pdfio_file_t *pdf, off_t xref_offset, pdfio_password_cb_t password_cb, void *password_data);
static bool write_catalog(pdfio_file_t *pdf);
static bool write_pages(pdfio_file_t *pdf);
static bool write_trailer(pdfio_file_t *pdf);
@ -221,13 +221,14 @@ pdfioFileCreate(
return (NULL);
}
pdf->filename = strdup(filename);
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
pdf->filename = strdup(filename);
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->permissions = PDFIO_PERMISSION_ALL;
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
if (media_box)
{
@ -496,13 +497,14 @@ pdfioFileCreateOutput(
return (NULL);
}
pdf->filename = strdup("output.pdf");
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
pdf->filename = strdup("output.pdf");
pdf->version = strdup(version);
pdf->mode = _PDFIO_MODE_WRITE;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->permissions = PDFIO_PERMISSION_ALL;
pdf->bufptr = pdf->buffer;
pdf->bufend = pdf->buffer + sizeof(pdf->buffer);
if (media_box)
{
@ -834,6 +836,35 @@ pdfioFileGetPage(pdfio_file_t *pdf, // I - PDF file
}
//
// 'pdfioFileGetPermissions()' - Get the access permissions of a PDF file.
//
// This function returns the access permissions of a PDF file and (optionally)
// the type of encryption that has been used.
//
pdfio_permission_t // O - Permission bits
pdfioFileGetPermissions(
pdfio_file_t *pdf, // I - PDF file
pdfio_encryption_t *encryption) // O - Type of encryption used or `NULL` to ignore
{
// Range check input...
if (!pdf)
{
if (encryption)
*encryption = PDFIO_ENCRYPTION_NONE;
return (PDFIO_PERMISSION_ALL);
}
// Return values...
if (encryption)
*encryption = pdf->encryption;
return (pdf->permissions);
}
//
// 'pdfioFileGetProducer()' - Get the producer string for a PDF file.
//
@ -910,9 +941,6 @@ pdfioFileOpen(
off_t xref_offset; // Offset to xref table
(void)password_cb;
(void)password_data;
// Range check input...
if (!filename)
return (NULL);
@ -935,10 +963,11 @@ pdfioFileOpen(
return (NULL);
}
pdf->filename = strdup(filename);
pdf->mode = _PDFIO_MODE_READ;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->filename = strdup(filename);
pdf->mode = _PDFIO_MODE_READ;
pdf->error_cb = error_cb;
pdf->error_data = error_data;
pdf->permissions = PDFIO_PERMISSION_ALL;
// Open the file...
if ((pdf->fd = open(filename, O_RDONLY | O_BINARY)) < 0)
@ -985,7 +1014,7 @@ pdfioFileOpen(
xref_offset = (off_t)strtol(ptr + 9, NULL, 10);
if (!load_xref(pdf, xref_offset))
if (!load_xref(pdf, xref_offset, password_cb, password_data))
goto error;
return (pdf);
@ -1074,28 +1103,6 @@ pdfioFileSetPermissions(
const char *owner_password, // I - Owner password, if any
const char *user_password) // I - User password, if any
{
pdfio_dict_t *dict; // Encryption dictionary
size_t i, j; // Looping vars
_pdfio_md5_t md5; // MD5 context
uint8_t digest[16]; // 128-bit MD5 digest
_pdfio_rc4_t rc4; // RC4 encryption context
size_t len; // Length of password
uint8_t owner_pad[32], // Padded owner password
user_pad[32], // Padded user password
perm_bytes[4], // Permissions bytes
*file_id; // File ID bytes
size_t file_id_len; // Length of file ID
pdfio_dict_t *cf_dict, // CF dictionary
*filter_dict; // CryptFilter dictionary
static uint8_t pad[32] = // Padding for passwords
{
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 (!pdf)
return (false);
@ -1108,182 +1115,7 @@ pdfioFileSetPermissions(
if (encryption == PDFIO_ENCRYPTION_NONE)
return (true);
if ((dict = pdfioDictCreate(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to create encryption dictionary.");
return (false);
}
pdfioDictSetName(dict, "Filter", "Standard");
switch (encryption)
{
case PDFIO_ENCRYPTION_RC4_128 :
case PDFIO_ENCRYPTION_AES_128 :
// Create the 128-bit encryption keys...
if (user_password)
{
// Use the specified user password
if ((len = strlen(user_password)) > sizeof(user_pad))
len = sizeof(user_pad);
}
else
{
// No user password
len = 0;
}
if (len > 0)
memcpy(user_pad, user_password, len);
if (len < sizeof(user_pad))
memcpy(user_pad + len, pad, sizeof(user_pad) - len);
if (owner_password)
{
// Use the specified owner password...
if ((len = strlen(owner_password)) > sizeof(owner_pad))
len = sizeof(owner_pad);
}
else if (user_password && *user_password)
{
// Generate a random owner password...
_pdfioCryptoMakeRandom(owner_pad, sizeof(owner_pad));
len = sizeof(owner_pad);
}
else
{
// No owner password
len = 0;
}
if (len > 0)
memcpy(owner_pad, owner_password, len);
if (len < sizeof(owner_pad))
memcpy(owner_pad + len, pad, sizeof(owner_pad) - len);
// Compute the owner key...
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, owner_pad, 32);
_pdfioCryptoMD5Finish(&md5, digest);
for (i = 0; i < 50; i ++)
{
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, digest, 16);
_pdfioCryptoMD5Finish(&md5, digest);
}
// Copy and encrypt the padded user password...
memcpy(pdf->owner_key, user_pad, sizeof(pdf->owner_key));
for (i = 0; i < 20; i ++)
{
uint8_t encrypt_key[16]; // RC4 encryption key
// XOR each byte in the digest with the loop counter to make a key...
for (j = 0; j < sizeof(encrypt_key); j ++)
encrypt_key[j] = (uint8_t)(digest[j] ^ i);
_pdfioCryptoRC4Init(&rc4, encrypt_key, sizeof(encrypt_key));
_pdfioCryptoRC4Crypt(&rc4, pdf->owner_key, pdf->owner_key, sizeof(pdf->owner_key));
}
// Generate the encryption key
perm_bytes[0] = (uint8_t)permissions;
perm_bytes[1] = (uint8_t)(permissions >> 8);
perm_bytes[2] = (uint8_t)(permissions >> 16);
perm_bytes[3] = (uint8_t)(permissions >> 24);
file_id = pdfioArrayGetBinary(pdf->id_array, 0, &file_id_len);
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, user_pad, 32);
_pdfioCryptoMD5Append(&md5, pdf->owner_key, 32);
_pdfioCryptoMD5Append(&md5, perm_bytes, 4);
_pdfioCryptoMD5Append(&md5, file_id, file_id_len);
_pdfioCryptoMD5Finish(&md5, digest);
// MD5 the result 50 times..
for (i = 0; i < 50; i ++)
{
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, digest, 16);
_pdfioCryptoMD5Finish(&md5, digest);
}
memcpy(pdf->encryption_key, digest, 16);
// Generate the user key...
_pdfioCryptoMD5Init(&md5);
_pdfioCryptoMD5Append(&md5, pad, 32);
_pdfioCryptoMD5Append(&md5, file_id, file_id_len);
_pdfioCryptoMD5Finish(&md5, pdf->user_key);
memset(pdf->user_key + 16, 0, 16);
// Encrypt the result 20 times...
for (i = 0; i < 20; i ++)
{
// XOR each byte in the key with the loop counter...
for (j = 0; j < 16; j ++)
digest[j] = (uint8_t)(pdf->encryption_key[j] ^ i);
_pdfioCryptoRC4Init(&rc4, digest, 16);
_pdfioCryptoRC4Crypt(&rc4, pdf->user_key, pdf->user_key, sizeof(pdf->user_key));
}
// Save everything in the dictionary...
pdfioDictSetNumber(dict, "Length", 128);
pdfioDictSetBinary(dict, "O", pdf->owner_key, sizeof(pdf->owner_key));
pdfioDictSetNumber(dict, "P", (int)permissions);
pdfioDictSetNumber(dict, "R", encryption == PDFIO_ENCRYPTION_RC4_128 ? 3 : 4);
pdfioDictSetNumber(dict, "V", encryption == PDFIO_ENCRYPTION_RC4_128 ? 2 : 4);
pdfioDictSetBinary(dict, "U", pdf->user_key, sizeof(pdf->user_key));
if (encryption == PDFIO_ENCRYPTION_AES_128)
{
if ((cf_dict = pdfioDictCreate(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to create Encryption CF dictionary.");
return (false);
}
if ((filter_dict = pdfioDictCreate(pdf)) == NULL)
{
_pdfioFileError(pdf, "Unable to create Encryption CryptFilter dictionary.");
return (false);
}
pdfioDictSetName(filter_dict, "Type", "CryptFilter");
pdfioDictSetName(filter_dict, "CFM", encryption == PDFIO_ENCRYPTION_RC4_128 ? "V2" : "AESV2");
pdfioDictSetDict(cf_dict, "PDFio", filter_dict);
pdfioDictSetDict(dict, "CF", cf_dict);
pdfioDictSetName(dict, "StmF", "PDFio");
pdfioDictSetName(dict, "StrF", "PDFio");
pdfioDictSetBoolean(dict, "EncryptMetadata", true);
}
break;
case PDFIO_ENCRYPTION_AES_256 :
// TODO: Implement AES-256 (/V 6 /R 6)
default :
_pdfioFileError(pdf, "Encryption mode %d not supported for writing.", (int)encryption);
return (false);
}
if ((pdf->encrypt_obj = pdfioFileCreateObj(pdf, dict)) == NULL)
{
_pdfioFileError(pdf, "Unable to create encryption object.");
return (false);
}
pdfioObjClose(pdf->encrypt_obj);
pdf->encryption = encryption;
pdf->permissions = permissions;
return (true);
return (_pdfioCryptoLock(pdf, permissions, encryption, owner_password, user_password));
}
@ -1567,8 +1399,11 @@ load_pages(pdfio_file_t *pdf, // I - PDF file
//
static bool // O - `true` on success, `false` on failure
load_xref(pdfio_file_t *pdf, // I - PDF file
off_t xref_offset) // I - Offset to xref
load_xref(
pdfio_file_t *pdf, // I - PDF file
off_t xref_offset, // I - Offset to xref
pdfio_password_cb_t password_cb, // I - Password callback or `NULL` for none
void *password_data) // I - Password callback data, if any
{
bool done = false; // Are we done?
char line[1024], // Line from file
@ -1668,13 +1503,6 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Cross-reference stream does not have a dictionary.");
return (false);
}
else if (_pdfioDictGetValue(pdf->trailer_dict, "Encrypt"))
{
// TODO: Fix me
// Encryption not yet supported...
_pdfioFileError(pdf, "Sorry, PDFio currently does not support encrypted PDF files.");
return (false);
}
obj->value = trailer;
@ -1809,6 +1637,10 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
pdfioStreamClose(st);
// If the trailer contains an Encrypt key, try unlocking the file...
if ((pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
return (false);
// Load any object streams that are left...
PDFIO_DEBUG("load_xref: %lu compressed object streams to load.\n", (unsigned long)num_sobjs);
@ -1916,12 +1748,6 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
_pdfioFileError(pdf, "Trailer is not a dictionary.");
return (false);
}
else if (_pdfioDictGetValue(pdf->trailer_dict, "Encrypt"))
{
// Encryption not yet supported...
_pdfioFileError(pdf, "Sorry, PDFio currently does not support encrypted PDF files.");
return (false);
}
_pdfioTokenFlush(&tb);
}
@ -1941,6 +1767,10 @@ load_xref(pdfio_file_t *pdf, // I - PDF file
// Save the trailer dictionary and grab the root (catalog) and info
// objects...
pdf->trailer_dict = trailer.value.dict;
// If the trailer contains an Encrypt key, try unlocking the file...
if ((pdf->encrypt_obj = pdfioDictGetObj(pdf->trailer_dict, "Encrypt")) != NULL && !_pdfioCryptoUnlock(pdf, password_cb, password_data))
return (false);
}
if ((xref_offset = (off_t)pdfioDictGetNumber(trailer.value.dict, "Prev")) <= 0)

View File

@ -259,6 +259,9 @@ struct _pdfio_file_s // PDF file structure
uint8_t encryption_key[16], // Object encryption key
owner_key[32], // Owner encryption key
user_key[32]; // User encryption key
size_t encryption_keylen, // Length of encryption key
owner_keylen, // Length of owner encryption key
user_keylen; // Length of user encryption key
// Active file data
int fd; // File descriptor
@ -344,6 +347,7 @@ extern bool _pdfioArrayWrite(pdfio_array_t *a, pdfio_obj_t *obj) _PDFIO_INTERNA
extern void _pdfioCryptoAESInit(_pdfio_aes_t *ctx, const uint8_t *key, size_t keylen, const uint8_t *iv) _PDFIO_INTERNAL;
extern size_t _pdfioCryptoAESDecrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern size_t _pdfioCryptoAESEncrypt(_pdfio_aes_t *ctx, uint8_t *outbuffer, const uint8_t *inbuffer, size_t len) _PDFIO_INTERNAL;
extern bool _pdfioCryptoLock(pdfio_file_t *pdf, pdfio_permission_t permissions, pdfio_encryption_t encryption, const char *owner_password, const char *user_password) _PDFIO_INTERNAL;
extern void _pdfioCryptoMakeRandom(uint8_t *buffer, size_t bytes) _PDFIO_INTERNAL;
extern _pdfio_crypto_cb_t _pdfioCryptoMakeReader(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL;
extern _pdfio_crypto_cb_t _pdfioCryptoMakeWriter(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_crypto_ctx_t *ctx, uint8_t *iv, size_t *ivlen) _PDFIO_INTERNAL;
@ -355,6 +359,7 @@ extern size_t _pdfioCryptoRC4Crypt(_pdfio_rc4_t *ctx, uint8_t *outbuffer, const
extern void _pdfioCryptoSHA256Append(_pdfio_sha256_t *, const uint8_t *bytes, size_t bytecount) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Init(_pdfio_sha256_t *ctx) _PDFIO_INTERNAL;
extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Digest) _PDFIO_INTERNAL;
extern bool _pdfioCryptoUnlock(pdfio_file_t *pdf, pdfio_password_cb_t password_cb, void *password_data) _PDFIO_INTERNAL;
extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL;
extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL;

View File

@ -121,7 +121,7 @@ pdfioStreamClose(pdfio_stream_t *st) // I - Stream
uint8_t temp[8192]; // Temporary buffer
size_t outbytes; // Output bytes
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, st->buffer, (size_t)(st->bufptr - st->buffer));
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, (uint8_t *)st->buffer, (size_t)(st->bufptr - st->buffer));
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
{
ret = false;
@ -423,6 +423,24 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
return (NULL);
}
if (obj->pdf->encryption)
{
uint8_t iv[64]; // Initialization vector
size_t ivlen; // Length of initialization vector, if any
ivlen = _pdfioFilePeek(st->pdf, iv, sizeof(iv));
if ((st->crypto_cb = _pdfioCryptoMakeReader(st->pdf, obj, &st->crypto_ctx, iv, &ivlen)) == NULL)
{
// TODO: Add error message?
free(st);
return (NULL);
}
if (ivlen > 0)
_pdfioFileConsume(st->pdf, ivlen);
}
if (decode)
{
// Try to decode/decompress the contents of this object...
@ -537,6 +555,9 @@ _pdfioStreamOpen(pdfio_obj_t *obj, // I - Object
return (NULL);
}
if (st->crypto_cb)
rbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, rbytes);
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
@ -810,7 +831,7 @@ pdfioStreamWrite(
if (st->bufptr >= st->bufend)
{
// Encrypt and flush
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, st->buffer, sizeof(st->buffer));
outbytes = (st->crypto_cb)(&st->crypto_ctx, temp, (uint8_t *)st->buffer, sizeof(st->buffer));
if (!_pdfioFileWrite(st->pdf, temp, outbytes))
return (false);
@ -980,8 +1001,13 @@ stream_read(pdfio_stream_t *st, // I - Stream
rbytes = _pdfioFileRead(st->pdf, buffer, bytes);
if (rbytes > 0)
{
st->remaining -= (size_t)rbytes;
if (st->crypto_cb)
(st->crypto_cb)(&st->crypto_ctx, (uint8_t *)buffer, (uint8_t *)buffer, rbytes);
}
return (rbytes);
}
else if (st->filter == PDFIO_FILTER_FLATE)
@ -1005,6 +1031,9 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
@ -1058,6 +1087,9 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;
@ -1122,6 +1154,9 @@ stream_read(pdfio_stream_t *st, // I - Stream
if (rbytes <= 0)
return (-1); // End of file...
if (st->crypto_cb)
rbytes = (st->crypto_cb)(&st->crypto_ctx, st->cbuffer, st->cbuffer, rbytes);
st->remaining -= (size_t)rbytes;
st->flate.next_in = (Bytef *)st->cbuffer;
st->flate.avail_in = (uInt)rbytes;

View File

@ -196,7 +196,7 @@ extern size_t pdfioFileGetNumObjs(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern size_t pdfioFileGetNumPages(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileGetObj(pdfio_file_t *pdf, size_t n) _PDFIO_PUBLIC;
extern pdfio_obj_t *pdfioFileGetPage(pdfio_file_t *pdf, size_t n) _PDFIO_PUBLIC;
extern pdfio_permission_t pdfioFileGetPermissions(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern pdfio_permission_t pdfioFileGetPermissions(pdfio_file_t *pdf, pdfio_encryption_t *encryption) _PDFIO_PUBLIC;
extern const char *pdfioFileGetProducer(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetSubject(pdfio_file_t *pdf) _PDFIO_PUBLIC;
extern const char *pdfioFileGetTitle(pdfio_file_t *pdf) _PDFIO_PUBLIC;