From dd56317635723a7d1ab4f9f8251730408a9b3585 Mon Sep 17 00:00:00 2001 From: Michael R Sweet Date: Sat, 23 Oct 2021 18:08:16 -0400 Subject: [PATCH] Need object when reading/writing encrypted PDFs (to decrypt/encrypt strings), RC4 writing is now working, AES-128 needs work, AES-256 hasn't been done yet. --- pdfio-array.c | 8 ++-- pdfio-crypto.c | 2 + pdfio-dict.c | 6 ++- pdfio-file.c | 82 ++++++++++++++++++--------------- pdfio-object.c | 4 +- pdfio-private.h | 12 ++--- pdfio-value.c | 119 ++++++++++++++++++++++++++++++++++++++++++++---- testpdfio.c | 4 +- 8 files changed, 175 insertions(+), 62 deletions(-) diff --git a/pdfio-array.c b/pdfio-array.c index 6f91699..ae97a9c 100644 --- a/pdfio-array.c +++ b/pdfio-array.c @@ -560,6 +560,7 @@ _pdfioArrayGetValue(pdfio_array_t *a, // I - Array pdfio_array_t * // O - New array _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file + pdfio_obj_t *obj, // I - Object, if any _pdfio_token_t *tb) // I - Token buffer/stack { pdfio_array_t *array; // New array @@ -584,7 +585,7 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file // Push the token and decode the value... _pdfioTokenPush(tb, token); - if (!_pdfioValueRead(pdf, tb, &value)) + if (!_pdfioValueRead(pdf, obj, tb, &value)) break; // PDFIO_DEBUG("_pdfioArrayRead(%p): Appending ", (void *)array); @@ -603,7 +604,8 @@ _pdfioArrayRead(pdfio_file_t *pdf, // I - PDF file // bool // O - `true` on success, `false` otherwise -_pdfioArrayWrite(pdfio_array_t *a) // I - Array +_pdfioArrayWrite(pdfio_array_t *a, // I - Array + pdfio_obj_t *obj) // I - Object, if any { pdfio_file_t *pdf = a->pdf; // PDF file size_t i; // Looping var @@ -617,7 +619,7 @@ _pdfioArrayWrite(pdfio_array_t *a) // I - Array // Write each value... for (i = a->num_values, v = a->values; i > 0; i --, v ++) { - if (!_pdfioValueWrite(pdf, v, NULL)) + if (!_pdfioValueWrite(pdf, obj, v, NULL)) return (false); } diff --git a/pdfio-crypto.c b/pdfio-crypto.c index 2632ed9..410a98f 100644 --- a/pdfio-crypto.c +++ b/pdfio-crypto.c @@ -244,6 +244,7 @@ _pdfio_crypto_cb_t // O - Decryption callback or `NULL` for none // Initialize the RC4/AES context using the digest... if (pdf->encryption == PDFIO_ENCRYPTION_RC4_128) { + *ivlen = 0; _pdfioCryptoRC4Init(&ctx->rc4, digest, sizeof(digest)); return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt); } @@ -305,6 +306,7 @@ _pdfio_crypto_cb_t // O - Encryption callback or `NULL` for none // Initialize the RC4/AES context using the digest... if (pdf->encryption == PDFIO_ENCRYPTION_RC4_128) { + *ivlen = 0; _pdfioCryptoRC4Init(&ctx->rc4, digest, sizeof(digest)); return ((_pdfio_crypto_cb_t)_pdfioCryptoRC4Crypt); } diff --git a/pdfio-dict.c b/pdfio-dict.c index b436e9f..a969fa8 100644 --- a/pdfio-dict.c +++ b/pdfio-dict.c @@ -420,6 +420,7 @@ _pdfioDictGetValue(pdfio_dict_t *dict, // I - Dictionary pdfio_dict_t * // O - New dictionary _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file + pdfio_obj_t *obj, // I - Object, if any _pdfio_token_t *tb) // I - Token buffer/stack { pdfio_dict_t *dict; // New dictionary @@ -448,7 +449,7 @@ _pdfioDictRead(pdfio_file_t *pdf, // I - PDF file } // Then get the next value... - if (!_pdfioValueRead(pdf, tb, &value)) + if (!_pdfioValueRead(pdf, obj, tb, &value)) { _pdfioFileError(pdf, "Missing value for dictionary key."); break; @@ -850,6 +851,7 @@ _pdfioDictSetValue( bool // O - `true` on success, `false` on failure _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary + pdfio_obj_t *obj, // I - Object, if any off_t *length) // I - Offset to length value { pdfio_file_t *pdf = dict->pdf; // PDF file @@ -877,7 +879,7 @@ _pdfioDictWrite(pdfio_dict_t *dict, // I - Dictionary if (!_pdfioFilePuts(pdf, " 9999999999")) return (false); } - else if (!_pdfioValueWrite(pdf, &pair->value, NULL)) + else if (!_pdfioValueWrite(pdf, obj, &pair->value, NULL)) return (false); } diff --git a/pdfio-file.c b/pdfio-file.c index c2cbc72..984cbaa 100644 --- a/pdfio-file.c +++ b/pdfio-file.c @@ -193,6 +193,7 @@ pdfioFileCreate( pdfio_file_t *pdf; // PDF file pdfio_dict_t *dict; // Dictionary for pages object pdfio_dict_t *info_dict; // Dictionary for information object + unsigned char id_value[16]; // File ID value // Range check input... @@ -303,6 +304,15 @@ pdfioFileCreate( return (NULL); } + // Create random file ID values... + _pdfioCryptoMakeRandom(id_value, sizeof(id_value)); + + if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL) + { + pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value)); + pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value)); + } + return (pdf); } @@ -458,6 +468,7 @@ pdfioFileCreateOutput( pdfio_file_t *pdf; // PDF file pdfio_dict_t *dict; // Dictionary for pages object pdfio_dict_t *info_dict; // Dictionary for information object + unsigned char id_value[16]; // File ID value // Range check input... @@ -558,6 +569,15 @@ pdfioFileCreateOutput( return (NULL); } + // Create random file ID values... + _pdfioCryptoMakeRandom(id_value, sizeof(id_value)); + + if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL) + { + pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value)); + pdfioArrayAppendBinary(pdf->id_array, id_value, sizeof(id_value)); + } + return (pdf); } @@ -1099,51 +1119,51 @@ pdfioFileSetPermissions( case PDFIO_ENCRYPTION_RC4_128 : case PDFIO_ENCRYPTION_AES_128 : // Create the 128-bit encryption keys... - if (user_password && *user_password) + if (user_password) { - // Copy the user password and pad it with the special PDF pad bytes + // Use the specified user password if ((len = strlen(user_password)) > sizeof(user_pad)) len = sizeof(user_pad); - - if (len > 0) - memcpy(user_pad, user_password, len); - if (len < sizeof(user_pad)) - memcpy(user_pad + len, pad, sizeof(user_pad) - len); } else { - // Use default (pad) password - memcpy(user_pad, pad, sizeof(user_pad)); + // No user password + len = 0; } - if (owner_password && *owner_password) + 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) { - // Copy the owner password and pad it with the special PDF pad bytes + // Use the specified owner password... if ((len = strlen(owner_password)) > sizeof(owner_pad)) len = sizeof(owner_pad); - - if (len > 0) - memcpy(owner_pad, owner_password, len); - if (len < sizeof(owner_pad)) - memcpy(owner_pad + len, pad, sizeof(owner_pad) - len); } else if (user_password && *user_password) { // Generate a random owner password... _pdfioCryptoMakeRandom(owner_pad, sizeof(owner_pad)); + len = sizeof(owner_pad); } else { - // Use default (pad) password - memcpy(owner_pad, pad, sizeof(owner_pad)); + // 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); - // MD5 the result 50 more times... for (i = 0; i < 50; i ++) { _pdfioCryptoMD5Init(&md5); @@ -1151,13 +1171,12 @@ pdfioFileSetPermissions( _pdfioCryptoMD5Finish(&md5, digest); } - // Copy the padded user password... + // Copy and encrypt the padded user password... memcpy(pdf->owner_key, user_pad, sizeof(pdf->owner_key)); - // Encrypt the result 20 times... for (i = 0; i < 20; i ++) { - uint8_t encrypt_key[16];// RC4 encryption key + 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 ++) @@ -1178,7 +1197,6 @@ pdfioFileSetPermissions( _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); @@ -1437,7 +1455,7 @@ load_obj_stream(pdfio_obj_t *obj) // I - Object to load // Read the objects themselves... for (cur_obj = 0; cur_obj < num_objs; cur_obj ++) { - if (!_pdfioValueRead(obj->pdf, &tb, &(objs[cur_obj]->value))) + if (!_pdfioValueRead(obj->pdf, obj, &tb, &(objs[cur_obj]->value))) { pdfioStreamClose(st); return (false); @@ -1615,7 +1633,7 @@ load_xref(pdfio_file_t *pdf, // I - PDF file _pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf); - if (!_pdfioValueRead(pdf, &tb, &trailer)) + if (!_pdfioValueRead(pdf, obj, &tb, &trailer)) { _pdfioFileError(pdf, "Unable to read cross-reference stream dictionary."); return (false); @@ -1863,7 +1881,7 @@ load_xref(pdfio_file_t *pdf, // I - PDF file _pdfioTokenInit(&tb, pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, pdf); - if (!_pdfioValueRead(pdf, &tb, &trailer)) + if (!_pdfioValueRead(pdf, NULL, &tb, &trailer)) { _pdfioFileError(pdf, "Unable to read trailer dictionary."); return (false); @@ -1982,7 +2000,6 @@ write_trailer(pdfio_file_t *pdf) // I - PDF file bool ret = true; // Return value off_t xref_offset; // Offset to xref table size_t i; // Looping var - unsigned char id_values[2][16]; // ID array values // Write the xref table... @@ -2016,15 +2033,6 @@ write_trailer(pdfio_file_t *pdf) // I - PDF file goto done; } - _pdfioCryptoMakeRandom(id_values[0], sizeof(id_values[0])); - _pdfioCryptoMakeRandom(id_values[1], sizeof(id_values[1])); - - if ((pdf->id_array = pdfioArrayCreate(pdf)) != NULL) - { - pdfioArrayAppendBinary(pdf->id_array, id_values[0], sizeof(id_values[0])); - pdfioArrayAppendBinary(pdf->id_array, id_values[1], sizeof(id_values[1])); - } - if ((pdf->trailer_dict = pdfioDictCreate(pdf)) == NULL) { _pdfioFileError(pdf, "Unable to create trailer."); @@ -2040,7 +2048,7 @@ write_trailer(pdfio_file_t *pdf) // I - PDF file pdfioDictSetObj(pdf->trailer_dict, "Root", pdf->root_obj); pdfioDictSetNumber(pdf->trailer_dict, "Size", pdf->num_objs + 1); - if (!_pdfioDictWrite(pdf->trailer_dict, NULL)) + if (!_pdfioDictWrite(pdf->trailer_dict, NULL, NULL)) { _pdfioFileError(pdf, "Unable to write trailer."); ret = false; diff --git a/pdfio-object.c b/pdfio-object.c index 9059ff8..9848ba5 100644 --- a/pdfio-object.c +++ b/pdfio-object.c @@ -409,7 +409,7 @@ _pdfioObjLoad(pdfio_obj_t *obj) // I - Object // Then grab the object value... _pdfioTokenInit(&tb, obj->pdf, (_pdfio_tconsume_cb_t)_pdfioFileConsume, (_pdfio_tpeek_cb_t)_pdfioFilePeek, obj->pdf); - if (!_pdfioValueRead(obj->pdf, &tb, &obj->value)) + if (!_pdfioValueRead(obj->pdf, obj, &tb, &obj->value)) { _pdfioFileError(obj->pdf, "Unable to read value for object %lu.", (unsigned long)obj->number); return (false); @@ -479,7 +479,7 @@ write_obj_header(pdfio_obj_t *obj) // I - Object if (!_pdfioFilePrintf(obj->pdf, "%lu %u obj\n", (unsigned long)obj->number, obj->generation)) return (false); - if (!_pdfioValueWrite(obj->pdf, &obj->value, &obj->length_offset)) + if (!_pdfioValueWrite(obj->pdf, obj, &obj->value, &obj->length_offset)) return (false); return (_pdfioFilePuts(obj->pdf, "\n")); diff --git a/pdfio-private.h b/pdfio-private.h index 80f3b03..dd3975b 100644 --- a/pdfio-private.h +++ b/pdfio-private.h @@ -338,8 +338,8 @@ struct _pdfio_stream_s // Stream extern void _pdfioArrayDebug(pdfio_array_t *a, FILE *fp) _PDFIO_INTERNAL; extern void _pdfioArrayDelete(pdfio_array_t *a) _PDFIO_INTERNAL; extern _pdfio_value_t *_pdfioArrayGetValue(pdfio_array_t *a, size_t n) _PDFIO_INTERNAL; -extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, _pdfio_token_t *ts) _PDFIO_INTERNAL; -extern bool _pdfioArrayWrite(pdfio_array_t *a) _PDFIO_INTERNAL; +extern pdfio_array_t *_pdfioArrayRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts) _PDFIO_INTERNAL; +extern bool _pdfioArrayWrite(pdfio_array_t *a, pdfio_obj_t *obj) _PDFIO_INTERNAL; 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; @@ -359,9 +359,9 @@ extern void _pdfioCryptoSHA256Finish(_pdfio_sha256_t *ctx, uint8_t *Message_Dig extern void _pdfioDictDebug(pdfio_dict_t *dict, FILE *fp) _PDFIO_INTERNAL; extern void _pdfioDictDelete(pdfio_dict_t *dict) _PDFIO_INTERNAL; extern _pdfio_value_t *_pdfioDictGetValue(pdfio_dict_t *dict, const char *key) _PDFIO_INTERNAL; -extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, _pdfio_token_t *ts) _PDFIO_INTERNAL; +extern pdfio_dict_t *_pdfioDictRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts) _PDFIO_INTERNAL; extern bool _pdfioDictSetValue(pdfio_dict_t *dict, const char *key, _pdfio_value_t *value) _PDFIO_INTERNAL; -extern bool _pdfioDictWrite(pdfio_dict_t *dict, off_t *length) _PDFIO_INTERNAL; +extern bool _pdfioDictWrite(pdfio_dict_t *dict, pdfio_obj_t *obj, off_t *length) _PDFIO_INTERNAL; extern bool _pdfioFileAddMappedObj(pdfio_file_t *pdf, pdfio_obj_t *dst_obj, pdfio_obj_t *src_obj) _PDFIO_INTERNAL; extern bool _pdfioFileAddPage(pdfio_file_t *pdf, pdfio_obj_t *obj) _PDFIO_INTERNAL; @@ -399,7 +399,7 @@ extern bool _pdfioTokenRead(_pdfio_token_t *tb, char *buffer, size_t bufsize); extern _pdfio_value_t *_pdfioValueCopy(pdfio_file_t *pdfdst, _pdfio_value_t *vdst, pdfio_file_t *pdfsrc, _pdfio_value_t *vsrc) _PDFIO_INTERNAL; extern void _pdfioValueDebug(_pdfio_value_t *v, FILE *fp) _PDFIO_INTERNAL; extern void _pdfioValueDelete(_pdfio_value_t *v) _PDFIO_INTERNAL; -extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, _pdfio_token_t *ts, _pdfio_value_t *v) _PDFIO_INTERNAL; -extern bool _pdfioValueWrite(pdfio_file_t *pdf, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL; +extern _pdfio_value_t *_pdfioValueRead(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_token_t *ts, _pdfio_value_t *v) _PDFIO_INTERNAL; +extern bool _pdfioValueWrite(pdfio_file_t *pdf, pdfio_obj_t *obj, _pdfio_value_t *v, off_t *length) _PDFIO_INTERNAL; #endif // !PDFIO_PRIVATE_H diff --git a/pdfio-value.c b/pdfio-value.c index 784aa61..a4651be 100644 --- a/pdfio-value.c +++ b/pdfio-value.c @@ -194,6 +194,7 @@ _pdfioValueDelete(_pdfio_value_t *v) // I - Value _pdfio_value_t * // O - Value or `NULL` on error/EOF _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file + pdfio_obj_t *obj, // I - Object, if any _pdfio_token_t *tb, // I - Token buffer/stack _pdfio_value_t *v) // I - Value { @@ -216,7 +217,8 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file #endif // DEBUG - PDFIO_DEBUG("_pdfioValueRead(pdf=%p, v=%p)\n", pdf, v); + PDFIO_DEBUG("_pdfioValueRead(pdf=%p, obj=%p, v=%p)\n", pdf, obj, v); + (void)obj; // TODO: Implement decryption if (!_pdfioTokenGet(tb, token, sizeof(token))) return (NULL); @@ -225,14 +227,14 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file { // Start of array v->type = PDFIO_VALTYPE_ARRAY; - if ((v->value.array = _pdfioArrayRead(pdf, tb)) == NULL) + if ((v->value.array = _pdfioArrayRead(pdf, obj, tb)) == NULL) return (NULL); } else if (!strcmp(token, "<<")) { // Start of dictionary v->type = PDFIO_VALTYPE_DICT; - if ((v->value.dict = _pdfioDictRead(pdf, tb)) == NULL) + if ((v->value.dict = _pdfioDictRead(pdf, obj, tb)) == NULL) return (NULL); } else if (!strncmp(token, "(D:", 3)) @@ -474,6 +476,7 @@ _pdfioValueRead(pdfio_file_t *pdf, // I - PDF file bool // O - `true` on success, `false` on failure _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file + pdfio_obj_t *obj, // I - Object, if any _pdfio_value_t *v, // I - Value off_t *length)// O - Offset to /Length value, if any { @@ -483,23 +486,47 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file return (false); case PDFIO_VALTYPE_ARRAY : - return (_pdfioArrayWrite(v->value.array)); + return (_pdfioArrayWrite(v->value.array, obj)); case PDFIO_VALTYPE_BINARY : { - size_t i; // Looping var - unsigned char *dataptr; // Pointer into data + size_t databytes; // Bytes to write + uint8_t temp[32768], // Temporary buffer for encryption + *dataptr; // Pointer into data + + if (obj && pdf->encryption) + { + // Write encrypted string... + _pdfio_crypto_ctx_t ctx; // Encryption context + _pdfio_crypto_cb_t cb; // Encryption callback + size_t ivlen; // Number of initialization vector bytes + + if (v->value.binary.datalen > (sizeof(temp) - 32)) + { + _pdfioFileError(pdf, "Unable to write encrypted binary string - too long."); + return (false); + } + + cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen); + databytes = (cb)(&ctx, temp + ivlen, v->value.binary.data, v->value.binary.datalen) + ivlen; + dataptr = temp; + } + else + { + dataptr = v->value.binary.data; + databytes = v->value.binary.datalen; + } if (!_pdfioFilePuts(pdf, "<")) return (false); - for (i = v->value.binary.datalen, dataptr = v->value.binary.data; i > 1; i -= 2, dataptr += 2) + for (; databytes > 1; databytes -= 2, dataptr += 2) { if (!_pdfioFilePrintf(pdf, "%02X%02X", dataptr[0], dataptr[1])) return (false); } - if (i > 0) + if (databytes > 0) return (_pdfioFilePrintf(pdf, "%02X>", dataptr[0])); else return (_pdfioFilePuts(pdf, ">")); @@ -514,6 +541,7 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file case PDFIO_VALTYPE_DATE : { struct tm date; // Date values + char datestr[32]; // Formatted date value #ifdef _WIN32 gmtime_s(&date, &v->value.date); @@ -521,11 +549,45 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file gmtime_r(&v->value.date, &date); #endif // _WIN32 - return (_pdfioFilePrintf(pdf, "(D:%04d%02d%02d%02d%02d%02dZ)", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec)); + snprintf(datestr, sizeof(datestr), "D:%04d%02d%02d%02d%02d%02dZ", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday, date.tm_hour, date.tm_min, date.tm_sec); + + if (obj && pdf->encryption) + { + // Write encrypted string... + uint8_t temp[32768], // Encrypted bytes + *tempptr; // Pointer into encrypted bytes + _pdfio_crypto_ctx_t ctx; // Encryption context + _pdfio_crypto_cb_t cb; // Encryption callback + size_t len = strlen(datestr), + // Length of value + ivlen, // Number of initialization vector bytes + tempbytes; // Number of output bytes + + cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen); + tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)datestr, len) + ivlen; + + if (!_pdfioFilePuts(pdf, "<")) + return (false); + + for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2) + { + if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1])) + return (false); + } + + if (tempbytes > 0) + return (_pdfioFilePrintf(pdf, "%02X>", *tempptr)); + else + return (_pdfioFilePuts(pdf, ">")); + } + else + { + return (_pdfioFilePrintf(pdf, "(%s)", datestr)); + } } case PDFIO_VALTYPE_DICT : - return (_pdfioDictWrite(v->value.dict, length)); + return (_pdfioDictWrite(v->value.dict, obj, length)); case PDFIO_VALTYPE_INDIRECT : return (_pdfioFilePrintf(pdf, " %lu %u R", (unsigned long)v->value.indirect.number, v->value.indirect.generation)); @@ -540,7 +602,44 @@ _pdfioValueWrite(pdfio_file_t *pdf, // I - PDF file return (_pdfioFilePrintf(pdf, " %g", v->value.number)); case PDFIO_VALTYPE_STRING : + if (obj && pdf->encryption) { + // Write encrypted string... + uint8_t temp[32768], // Encrypted bytes + *tempptr; // Pointer into encrypted bytes + _pdfio_crypto_ctx_t ctx; // Encryption context + _pdfio_crypto_cb_t cb; // Encryption callback + size_t len = strlen(v->value.string), + // Length of value + ivlen, // Number of initialization vector bytes + tempbytes; // Number of output bytes + + if (len > (sizeof(temp) - 32)) + { + _pdfioFileError(pdf, "Unable to write encrypted string - too long."); + return (false); + } + + cb = _pdfioCryptoMakeWriter(pdf, obj, &ctx, temp, &ivlen); + tempbytes = (cb)(&ctx, temp + ivlen, (const uint8_t *)v->value.string, len) + ivlen; + + if (!_pdfioFilePuts(pdf, "<")) + return (false); + + for (tempptr = temp; tempbytes > 1; tempbytes -= 2, tempptr += 2) + { + if (!_pdfioFilePrintf(pdf, "%02X%02X", tempptr[0], tempptr[1])) + return (false); + } + + if (tempbytes > 0) + return (_pdfioFilePrintf(pdf, "%02X>", *tempptr)); + else + return (_pdfioFilePuts(pdf, ">")); + } + else + { + // Write unencrypted string... const char *start, // Start of fragment *end; // End of fragment diff --git a/testpdfio.c b/testpdfio.c index 70eeaa9..832f306 100644 --- a/testpdfio.c +++ b/testpdfio.c @@ -970,7 +970,7 @@ do_unit_tests(void) fputs("_pdfioValueRead(complex_dict): ", stdout); s = complex_dict; _pdfioTokenInit(&tb, inpdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s); - if (_pdfioValueRead(inpdf, &tb, &value)) + if (_pdfioValueRead(inpdf, NULL, &tb, &value)) { // TODO: Check value... fputs("PASS: ", stdout); @@ -984,7 +984,7 @@ do_unit_tests(void) fputs("_pdfioValueRead(cid_dict): ", stdout); s = cid_dict; _pdfioTokenInit(&tb, inpdf, (_pdfio_tconsume_cb_t)token_consume_cb, (_pdfio_tpeek_cb_t)token_peek_cb, (void *)&s); - if (_pdfioValueRead(inpdf, &tb, &value)) + if (_pdfioValueRead(inpdf, NULL, &tb, &value)) { // TODO: Check value... fputs("PASS: ", stdout);