2
1
mirror of https://github.com/qpdf/qpdf.git synced 2025-02-02 11:58:25 +00:00

Make a static version of QPDF::pipeStreamData

This is in preparation of being able to pipe a stream's data without
keeping a copy of its containing qpdf object.
This commit is contained in:
Jay Berkenbilt 2019-01-06 10:34:52 -05:00
parent 7588cac295
commit fbbb0ee016
4 changed files with 134 additions and 83 deletions

View File

@ -692,6 +692,30 @@ class QPDF
}; };
friend class ResolveRecorder; friend class ResolveRecorder;
class EncryptionParameters
{
friend class QPDF;
public:
EncryptionParameters();
private:
bool encrypted;
bool encryption_initialized;
int encryption_V;
int encryption_R;
bool encrypt_metadata;
std::map<std::string, encryption_method_e> crypt_filters;
encryption_method_e cf_stream;
encryption_method_e cf_string;
encryption_method_e cf_file;
std::string provided_password;
std::string user_password;
std::string encryption_key;
std::string cached_object_encryption_key;
int cached_key_objid;
int cached_key_generation;
};
void parse(char const* password); void parse(char const* password);
void warn(QPDFExc const& e); void warn(QPDFExc const& e);
void setTrailer(QPDFObjectHandle obj); void setTrailer(QPDFObjectHandle obj);
@ -735,6 +759,16 @@ class QPDF
Pipeline* pipeline, Pipeline* pipeline,
bool suppress_warnings, bool suppress_warnings,
bool will_retry); bool will_retry);
static bool pipeStreamData(PointerHolder<QPDF::EncryptionParameters> encp,
PointerHolder<InputSource> file,
QPDF& qpdf_for_warning,
int objid, int generation,
qpdf_offset_t offset, size_t length,
QPDFObjectHandle dict,
bool is_attachment_stream,
Pipeline* pipeline,
bool suppress_warnings,
bool will_retry);
// For QPDFWriter: // For QPDFWriter:
@ -776,9 +810,12 @@ class QPDF
bool check_duplicate); bool check_duplicate);
// methods to support encryption -- implemented in QPDF_encryption.cc // methods to support encryption -- implemented in QPDF_encryption.cc
encryption_method_e interpretCF(QPDFObjectHandle); static encryption_method_e interpretCF(
PointerHolder<EncryptionParameters> encp, QPDFObjectHandle);
void initializeEncryption(); void initializeEncryption();
std::string getKeyForObject(int objid, int generation, bool use_aes); static std::string getKeyForObject(
PointerHolder<EncryptionParameters> encp,
int objid, int generation, bool use_aes);
void decryptString(std::string&, int objid, int generation); void decryptString(std::string&, int objid, int generation);
static std::string compute_encryption_key_from_password( static std::string compute_encryption_key_from_password(
std::string const& password, EncryptionData const& data); std::string const& password, EncryptionData const& data);
@ -787,9 +824,12 @@ class QPDF
static std::string recover_encryption_key_with_password( static std::string recover_encryption_key_with_password(
std::string const& password, EncryptionData const& data, std::string const& password, EncryptionData const& data,
bool& perms_valid); bool& perms_valid);
void decryptStream( static void decryptStream(
Pipeline*& pipeline, int objid, int generation, PointerHolder<EncryptionParameters> encp,
QPDFObjectHandle& stream_dict, PointerHolder<InputSource> file,
QPDF& qpdf_for_warning, Pipeline*& pipeline,
int objid, int generation,
QPDFObjectHandle& stream_dict, bool is_attachment_stream,
std::vector<PointerHolder<Pipeline> >& heap); std::vector<PointerHolder<Pipeline> >& heap);
// Methods to support object copying // Methods to support object copying
@ -1160,30 +1200,6 @@ class QPDF
std::set<QPDFObjGen>& visited, bool top); std::set<QPDFObjGen>& visited, bool top);
void filterCompressedObjects(std::map<int, int> const& object_stream_data); void filterCompressedObjects(std::map<int, int> const& object_stream_data);
class EncryptionParameters
{
friend class QPDF;
public:
EncryptionParameters();
private:
bool encrypted;
bool encryption_initialized;
int encryption_V;
int encryption_R;
bool encrypt_metadata;
std::map<std::string, encryption_method_e> crypt_filters;
encryption_method_e cf_stream;
encryption_method_e cf_string;
encryption_method_e cf_file;
std::string provided_password;
std::string user_password;
std::string encryption_key;
std::string cached_object_encryption_key;
int cached_key_objid;
int cached_key_generation;
};
class Members class Members
{ {
friend class QPDF; friend class QPDF;

View File

@ -2512,34 +2512,40 @@ QPDF::getCompressibleObjGens()
} }
bool bool
QPDF::pipeStreamData(int objid, int generation, QPDF::pipeStreamData(PointerHolder<EncryptionParameters> encp,
PointerHolder<InputSource> file,
QPDF& qpdf_for_warning,
int objid, int generation,
qpdf_offset_t offset, size_t length, qpdf_offset_t offset, size_t length,
QPDFObjectHandle stream_dict, QPDFObjectHandle stream_dict,
bool is_attachment_stream,
Pipeline* pipeline, Pipeline* pipeline,
bool suppress_warnings, bool suppress_warnings,
bool will_retry) bool will_retry)
{ {
bool success = false;
std::vector<PointerHolder<Pipeline> > to_delete; std::vector<PointerHolder<Pipeline> > to_delete;
if (this->m->encp->encrypted) if (encp->encrypted)
{ {
decryptStream(pipeline, objid, generation, stream_dict, to_delete); decryptStream(encp, file, qpdf_for_warning,
pipeline, objid, generation,
stream_dict, is_attachment_stream, to_delete);
} }
bool success = false;
try try
{ {
this->m->file->seek(offset, SEEK_SET); file->seek(offset, SEEK_SET);
char buf[10240]; char buf[10240];
while (length > 0) while (length > 0)
{ {
size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length); size_t to_read = (sizeof(buf) < length ? sizeof(buf) : length);
size_t len = this->m->file->read(buf, to_read); size_t len = file->read(buf, to_read);
if (len == 0) if (len == 0)
{ {
throw QPDFExc(qpdf_e_damaged_pdf, throw QPDFExc(qpdf_e_damaged_pdf,
this->m->file->getName(), file->getName(),
this->m->last_object_description, "",
this->m->file->getLastOffset(), file->getLastOffset(),
"unexpected EOF reading stream data"); "unexpected EOF reading stream data");
} }
length -= len; length -= len;
@ -2552,7 +2558,7 @@ QPDF::pipeStreamData(int objid, int generation,
{ {
if (! suppress_warnings) if (! suppress_warnings)
{ {
warn(e); qpdf_for_warning.warn(e);
} }
} }
catch (std::exception& e) catch (std::exception& e)
@ -2560,17 +2566,19 @@ QPDF::pipeStreamData(int objid, int generation,
if (! suppress_warnings) if (! suppress_warnings)
{ {
QTC::TC("qpdf", "QPDF decoding error warning"); QTC::TC("qpdf", "QPDF decoding error warning");
warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), qpdf_for_warning.warn(
"", this->m->file->getLastOffset(), QPDFExc(qpdf_e_damaged_pdf, file->getName(),
"error decoding stream data for object " + "", file->getLastOffset(),
QUtil::int_to_string(objid) + " " + "error decoding stream data for object " +
QUtil::int_to_string(generation) + ": " + e.what())); QUtil::int_to_string(objid) + " " +
QUtil::int_to_string(generation) + ": " + e.what()));
if (will_retry) if (will_retry)
{ {
warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), qpdf_for_warning.warn(
"", this->m->file->getLastOffset(), QPDFExc(qpdf_e_damaged_pdf, file->getName(),
"stream will be re-processed without" "", file->getLastOffset(),
" filtering to avoid data loss")); "stream will be re-processed without"
" filtering to avoid data loss"));
} }
} }
} }
@ -2588,6 +2596,23 @@ QPDF::pipeStreamData(int objid, int generation,
return success; return success;
} }
bool
QPDF::pipeStreamData(int objid, int generation,
qpdf_offset_t offset, size_t length,
QPDFObjectHandle stream_dict,
Pipeline* pipeline,
bool suppress_warnings,
bool will_retry)
{
bool is_attachment_stream = this->m->attachment_streams.count(
QPDFObjGen(objid, generation));
return pipeStreamData(
this->m->encp, this->m->file, *this,
objid, generation, offset, length,
stream_dict, is_attachment_stream,
pipeline, suppress_warnings, will_retry);
}
void void
QPDF::findAttachmentStreams() QPDF::findAttachmentStreams()
{ {

View File

@ -764,14 +764,15 @@ QPDF::recover_encryption_key_with_password(
} }
QPDF::encryption_method_e QPDF::encryption_method_e
QPDF::interpretCF(QPDFObjectHandle cf) QPDF::interpretCF(
PointerHolder<EncryptionParameters> encp, QPDFObjectHandle cf)
{ {
if (cf.isName()) if (cf.isName())
{ {
std::string filter = cf.getName(); std::string filter = cf.getName();
if (this->m->encp->crypt_filters.count(filter) != 0) if (encp->crypt_filters.count(filter) != 0)
{ {
return this->m->encp->crypt_filters[filter]; return encp->crypt_filters[filter];
} }
else if (filter == "/Identity") else if (filter == "/Identity")
{ {
@ -1000,11 +1001,11 @@ QPDF::initializeEncryption()
QPDFObjectHandle StmF = encryption_dict.getKey("/StmF"); QPDFObjectHandle StmF = encryption_dict.getKey("/StmF");
QPDFObjectHandle StrF = encryption_dict.getKey("/StrF"); QPDFObjectHandle StrF = encryption_dict.getKey("/StrF");
QPDFObjectHandle EFF = encryption_dict.getKey("/EFF"); QPDFObjectHandle EFF = encryption_dict.getKey("/EFF");
this->m->encp->cf_stream = interpretCF(StmF); this->m->encp->cf_stream = interpretCF(this->m->encp, StmF);
this->m->encp->cf_string = interpretCF(StrF); this->m->encp->cf_string = interpretCF(this->m->encp, StrF);
if (EFF.isName()) if (EFF.isName())
{ {
this->m->encp->cf_file = interpretCF(EFF); this->m->encp->cf_file = interpretCF(this->m->encp, EFF);
} }
else else
{ {
@ -1068,26 +1069,28 @@ QPDF::initializeEncryption()
} }
std::string std::string
QPDF::getKeyForObject(int objid, int generation, bool use_aes) QPDF::getKeyForObject(
PointerHolder<EncryptionParameters> encp,
int objid, int generation, bool use_aes)
{ {
if (! this->m->encp->encrypted) if (! encp->encrypted)
{ {
throw std::logic_error( throw std::logic_error(
"request for encryption key in non-encrypted PDF"); "request for encryption key in non-encrypted PDF");
} }
if (! ((objid == this->m->encp->cached_key_objid) && if (! ((objid == encp->cached_key_objid) &&
(generation == this->m->encp->cached_key_generation))) (generation == encp->cached_key_generation)))
{ {
this->m->encp->cached_object_encryption_key = encp->cached_object_encryption_key =
compute_data_key(this->m->encp->encryption_key, objid, generation, compute_data_key(encp->encryption_key, objid, generation,
use_aes, this->m->encp->encryption_V, use_aes, encp->encryption_V,
this->m->encp->encryption_R); encp->encryption_R);
this->m->encp->cached_key_objid = objid; encp->cached_key_objid = objid;
this->m->encp->cached_key_generation = generation; encp->cached_key_generation = generation;
} }
return this->m->encp->cached_object_encryption_key; return encp->cached_object_encryption_key;
} }
void void
@ -1131,7 +1134,8 @@ QPDF::decryptString(std::string& str, int objid, int generation)
} }
} }
std::string key = getKeyForObject(objid, generation, use_aes); std::string key = getKeyForObject(
this->m->encp, objid, generation, use_aes);
try try
{ {
if (use_aes) if (use_aes)
@ -1175,8 +1179,12 @@ QPDF::decryptString(std::string& str, int objid, int generation)
} }
void void
QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, QPDF::decryptStream(PointerHolder<EncryptionParameters> encp,
PointerHolder<InputSource> file,
QPDF& qpdf_for_warning, Pipeline*& pipeline,
int objid, int generation,
QPDFObjectHandle& stream_dict, QPDFObjectHandle& stream_dict,
bool is_attachment_stream,
std::vector<PointerHolder<Pipeline> >& heap) std::vector<PointerHolder<Pipeline> >& heap)
{ {
std::string type; std::string type;
@ -1190,7 +1198,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
return; return;
} }
bool use_aes = false; bool use_aes = false;
if (this->m->encp->encryption_V >= 4) if (encp->encryption_V >= 4)
{ {
encryption_method_e method = e_unknown; encryption_method_e method = e_unknown;
std::string method_source = "/StmF from /Encrypt dictionary"; std::string method_source = "/StmF from /Encrypt dictionary";
@ -1206,7 +1214,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
"/CryptFilterDecodeParms")) "/CryptFilterDecodeParms"))
{ {
QTC::TC("qpdf", "QPDF_encryption stream crypt filter"); QTC::TC("qpdf", "QPDF_encryption stream crypt filter");
method = interpretCF(decode_parms.getKey("/Name")); method = interpretCF(encp, decode_parms.getKey("/Name"));
method_source = "stream's Crypt decode parameters"; method_source = "stream's Crypt decode parameters";
} }
} }
@ -1229,7 +1237,7 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
{ {
QTC::TC("qpdf", "QPDF_encrypt crypt array"); QTC::TC("qpdf", "QPDF_encrypt crypt array");
method = interpretCF( method = interpretCF(
crypt_params.getKey("/Name")); encp, crypt_params.getKey("/Name"));
method_source = "stream's Crypt " method_source = "stream's Crypt "
"decode parameters (array)"; "decode parameters (array)";
} }
@ -1241,21 +1249,21 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
if (method == e_unknown) if (method == e_unknown)
{ {
if ((! this->m->encp->encrypt_metadata) && (type == "/Metadata")) if ((! encp->encrypt_metadata) && (type == "/Metadata"))
{ {
QTC::TC("qpdf", "QPDF_encryption cleartext metadata"); QTC::TC("qpdf", "QPDF_encryption cleartext metadata");
method = e_none; method = e_none;
} }
else else
{ {
if (this->m->attachment_streams.count( if (is_attachment_stream)
QPDFObjGen(objid, generation)) > 0)
{ {
method = this->m->encp->cf_file; QTC::TC("qpdf", "QPDF_encryption attachment stream");
method = encp->cf_file;
} }
else else
{ {
method = this->m->encp->cf_stream; method = encp->cf_stream;
} }
} }
} }
@ -1279,19 +1287,20 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
default: default:
// filter local to this stream. // filter local to this stream.
warn(QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), qpdf_for_warning.warn(
this->m->last_object_description, QPDFExc(qpdf_e_damaged_pdf, file->getName(),
this->m->file->getLastOffset(), "", file->getLastOffset(),
"unknown encryption filter for streams" "unknown encryption filter for streams"
" (check " + method_source + ");" " (check " + method_source + ");"
" streams may be decrypted improperly")); " streams may be decrypted improperly"));
// To avoid repeated warnings, reset cf_stream. Assume // To avoid repeated warnings, reset cf_stream. Assume
// we'd want to use AES if V == 4. // we'd want to use AES if V == 4.
this->m->encp->cf_stream = e_aes; encp->cf_stream = e_aes;
use_aes = true;
break; break;
} }
} }
std::string key = getKeyForObject(objid, generation, use_aes); std::string key = getKeyForObject(encp, objid, generation, use_aes);
if (use_aes) if (use_aes)
{ {
QTC::TC("qpdf", "QPDF_encryption aes decode stream"); QTC::TC("qpdf", "QPDF_encryption aes decode stream");

View File

@ -406,3 +406,4 @@ qpdf image optimize no pipeline 0
qpdf image optimize no shrink 0 qpdf image optimize no shrink 0
qpdf image optimize too small 0 qpdf image optimize too small 0
QPDFFormFieldObjectHelper WinAnsi 0 QPDFFormFieldObjectHelper WinAnsi 0
QPDF_encryption attachment stream 0