diff options
-rw-r--r-- | include/qpdf/QPDFWriter.hh | 5 | ||||
-rw-r--r-- | include/qpdf/qpdf-c.h | 13 | ||||
-rw-r--r-- | libqpdf/Pl_AES_PDF.cc | 22 | ||||
-rw-r--r-- | libqpdf/QPDFWriter.cc | 9 | ||||
-rw-r--r-- | libqpdf/qpdf-c.cc | 26 | ||||
-rw-r--r-- | libqpdf/qpdf/Pl_AES_PDF.hh | 3 | ||||
-rw-r--r-- | qpdf/qpdf-ctest.c | 17 | ||||
-rw-r--r-- | qpdf/qpdf.cc | 11 | ||||
-rw-r--r-- | qpdf/qpdf.testcov | 2 | ||||
-rw-r--r-- | qpdf/qtest/qpdf.test | 1 | ||||
-rw-r--r-- | qpdf/qtest/qpdf/c-r4.pdf | bin | 0 -> 10881 bytes |
11 files changed, 107 insertions, 2 deletions
diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index e16c5f99..9d92ffbb 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -110,6 +110,11 @@ class DLL_EXPORT QPDFWriter // suites. void setStaticID(bool); + // Use a fixed initialization vector for AES-CBC encryption. This + // is not secure. It should be used only in test suites for + // creating predictable encrypted output. + void setStaticAesIV(bool); + // Suppress inclusion of comments indicating original object IDs // when writing QDF files. This can also be useful for testing, // particularly when using comparison of two qdf files to diff --git a/include/qpdf/qpdf-c.h b/include/qpdf/qpdf-c.h index fab8112b..8c112ba5 100644 --- a/include/qpdf/qpdf-c.h +++ b/include/qpdf/qpdf-c.h @@ -226,6 +226,12 @@ extern "C" { DLL_EXPORT void qpdf_set_static_ID(qpdf_data qpdf, QPDF_BOOL value); + /* Never use qpdf_set_static_aes_IV except in test suites to + * create predictable AES encrypted output. + */ + DLL_EXPORT + void qpdf_set_static_aes_IV(qpdf_data qpdf, QPDF_BOOL value); + DLL_EXPORT void qpdf_set_suppress_original_object_IDs( qpdf_data qpdf, QPDF_BOOL value); @@ -259,6 +265,13 @@ extern "C" { int print, int modify); DLL_EXPORT + void qpdf_set_r4_encryption_parameters( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + int print, int modify, + QPDF_BOOL encrypt_metadata, QPDF_BOOL use_aes); + + DLL_EXPORT void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value); DLL_EXPORT diff --git a/libqpdf/Pl_AES_PDF.cc b/libqpdf/Pl_AES_PDF.cc index 0ceb7df7..d9754981 100644 --- a/libqpdf/Pl_AES_PDF.cc +++ b/libqpdf/Pl_AES_PDF.cc @@ -12,6 +12,8 @@ # define srandom srand #endif +bool Pl_AES_PDF::use_static_iv = false; + Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, bool encrypt, unsigned char const key[key_size]) : Pipeline(identifier, next), @@ -52,6 +54,12 @@ Pl_AES_PDF::disableCBC() } void +Pl_AES_PDF::useStaticIV() +{ + use_static_iv = true; +} + +void Pl_AES_PDF::write(unsigned char* data, int len) { unsigned int bytes_left = len; @@ -116,9 +124,19 @@ Pl_AES_PDF::initializeVector() srandom((int)QUtil::get_current_time() ^ 0xcccc); seeded_random = true; } - for (unsigned int i = 0; i < this->buf_size; ++i) + if (use_static_iv) + { + for (unsigned int i = 0; i < this->buf_size; ++i) + { + this->cbc_block[i] = 14 * (1 + i); + } + } + else { - this->cbc_block[i] = (unsigned char)((random() & 0xff0) >> 4); + for (unsigned int i = 0; i < this->buf_size; ++i) + { + this->cbc_block[i] = (unsigned char)((random() & 0xff0) >> 4); + } } } diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 3c1640f7..320706b0 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -140,6 +140,15 @@ QPDFWriter::setStaticID(bool val) } void +QPDFWriter::setStaticAesIV(bool val) +{ + if (val) + { + Pl_AES_PDF::useStaticIV(); + } +} + +void QPDFWriter::setSuppressOriginalObjectIDs(bool val) { this->suppress_original_object_ids = val; diff --git a/libqpdf/qpdf-c.cc b/libqpdf/qpdf-c.cc index abc74c95..96de680f 100644 --- a/libqpdf/qpdf-c.cc +++ b/libqpdf/qpdf-c.cc @@ -313,6 +313,12 @@ void qpdf_set_static_ID(qpdf_data qpdf, QPDF_BOOL value) qpdf->qpdf_writer->setStaticID(value); } +void qpdf_set_static_aes_IV(qpdf_data qpdf, QPDF_BOOL value) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_set_static_aes_IV"); + qpdf->qpdf_writer->setStaticAesIV(value); +} + void qpdf_set_suppress_original_object_IDs( qpdf_data qpdf, QPDF_BOOL value) { @@ -356,6 +362,26 @@ void qpdf_set_r3_encryption_parameters( QPDFWriter::r3m_all)); } +void qpdf_set_r4_encryption_parameters( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + int print, int modify, QPDF_BOOL encrypt_metadata, QPDF_BOOL use_aes) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_set_r4_encryption_parameters"); + qpdf->qpdf_writer->setR4EncryptionParameters( + user_password, owner_password, + allow_accessibility, allow_extract, + ((print == QPDF_R3_PRINT_LOW) ? QPDFWriter::r3p_low : + (print == QPDF_R3_PRINT_NONE) ? QPDFWriter::r3p_none : + QPDFWriter::r3p_full), + ((modify == QPDF_R3_MODIFY_ANNOTATE) ? QPDFWriter::r3m_annotate : + (modify == QPDF_R3_MODIFY_FORM) ? QPDFWriter::r3m_form : + (modify == QPDF_R3_MODIFY_ASSEMBLY) ? QPDFWriter::r3m_assembly : + (modify == QPDF_R3_MODIFY_NONE) ? QPDFWriter::r3m_none : + QPDFWriter::r3m_all), + encrypt_metadata, use_aes); +} + void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value) { QTC::TC("qpdf", "qpdf-c called qpdf_set_linearization"); diff --git a/libqpdf/qpdf/Pl_AES_PDF.hh b/libqpdf/qpdf/Pl_AES_PDF.hh index 888ea752..084fca7b 100644 --- a/libqpdf/qpdf/Pl_AES_PDF.hh +++ b/libqpdf/qpdf/Pl_AES_PDF.hh @@ -20,12 +20,15 @@ class DLL_EXPORT Pl_AES_PDF: public Pipeline // For testing only; PDF always uses CBC void disableCBC(); + // For testing only: use a fixed initialization vector for CBC + static void useStaticIV(); private: void flush(bool discard_padding); void initializeVector(); static unsigned int const buf_size = 16; + static bool use_static_iv; bool encrypt; bool cbc_mode; diff --git a/qpdf/qpdf-ctest.c b/qpdf/qpdf-ctest.c index c3e1040b..ce785a3a 100644 --- a/qpdf/qpdf-ctest.c +++ b/qpdf/qpdf-ctest.c @@ -229,6 +229,22 @@ static void test14(char const* infile, report_errors(); } +static void test15(char const* infile, + char const* password, + char const* outfile, + char const* outfile2) +{ + qpdf_read(qpdf, infile, password); + qpdf_init_write(qpdf, outfile); + qpdf_set_static_ID(qpdf, QPDF_TRUE); + qpdf_set_static_aes_IV(qpdf, QPDF_TRUE); + qpdf_set_r4_encryption_parameters( + qpdf, "user2", "owner2", QPDF_TRUE, QPDF_TRUE, + QPDF_R3_PRINT_LOW, QPDF_R3_MODIFY_ALL, QPDF_TRUE, QPDF_TRUE); + qpdf_write(qpdf); + report_errors(); +} + int main(int argc, char* argv[]) { char* whoami = 0; @@ -278,6 +294,7 @@ int main(int argc, char* argv[]) (n == 12) ? test12 : (n == 13) ? test13 : (n == 14) ? test14 : + (n == 15) ? test15 : 0); if (fn == 0) diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index c54504b8..c614b5bf 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -146,6 +146,8 @@ These options can be useful for digging into PDF files or for use in\n\ automated test suites for software that uses the qpdf library.\n\ \n\ --static-id generate static /ID: FOR TESTING ONLY!\n\ +--static-aes-iv use a static initialization vector for AES-CBC\n\ + This is option is not secure! FOR TESTING ONLY!\n\ --no-original-object-ids suppress original object ID comments in qdf mode\n\ --show-encryption quickly show encryption parameters\n\ --check-linearization check file integrity and linearization status\n\ @@ -604,6 +606,7 @@ int main(int argc, char* argv[]) std::string force_version; bool static_id = false; + bool static_aes_iv = false; bool suppress_original_object_id = false; bool show_encryption = false; bool check_linearization = false; @@ -758,6 +761,10 @@ int main(int argc, char* argv[]) { static_id = true; } + else if (strcmp(arg, "static-aes-iv") == 0) + { + static_aes_iv = true; + } else if (strcmp(arg, "no-original-object-ids") == 0) { suppress_original_object_id = true; @@ -1049,6 +1056,10 @@ int main(int argc, char* argv[]) { w.setStaticID(true); } + if (static_aes_iv) + { + w.setStaticAesIV(true); + } if (suppress_original_object_id) { w.setSuppressOriginalObjectIDs(true); diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index d1cf3293..dd9ec5b1 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -168,3 +168,5 @@ QPDF_encryption cleartext metadata 0 QPDF_encryption aes decode stream 0 QPDFWriter forcing object stream disable 0 QPDFWriter forced version disabled encryption 0 +qpdf-c called qpdf_set_r4_encryption_parameters 0 +qpdf-c called qpdf_set_static_aes_IV 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 4ff06406..3e425254 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -954,6 +954,7 @@ $td->runtest("invalid password", my @cenc = ( [11, 'hybrid-xref.pdf', "''", 'r2', ""], [12, 'hybrid-xref.pdf', "''", 'r3', ""], + [15, 'hybrid-xref.pdf', "''", 'r4', ""], [13, 'c-r2.pdf', 'user1', 'decrypt with user', "user password: user1\n"], [13, 'c-r3.pdf', 'owner2', 'decrypt with owner', diff --git a/qpdf/qtest/qpdf/c-r4.pdf b/qpdf/qtest/qpdf/c-r4.pdf Binary files differnew file mode 100644 index 00000000..d985109f --- /dev/null +++ b/qpdf/qtest/qpdf/c-r4.pdf |