Commit 0575d77d77b0308535422ad2d3a1bf7c94baabb0
1 parent
ee3682f1
Add public QPDFWriter::copyEncryptionParameters
Method to copy encryption parameters from another file. Adapted from existing code to copy encryption parameters from the original file.
Showing
7 changed files
with
57 additions
and
8 deletions
ChangeLog
| 1 | 2012-07-14 Jay Berkenbilt <ejb@ql.org> | 1 | 2012-07-14 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * QPDFWriter: add public copyEncryptionParameters to allow copying | ||
| 4 | + encryption parameters from another file. | ||
| 5 | + | ||
| 3 | * QPDFWriter: detect if the user has inserted an indirect object | 6 | * QPDFWriter: detect if the user has inserted an indirect object |
| 4 | from another QPDF object and throw an exception directing the user | 7 | from another QPDF object and throw an exception directing the user |
| 5 | to copyForeignObject. | 8 | to copyForeignObject. |
TODO
| @@ -64,10 +64,9 @@ Next | @@ -64,10 +64,9 @@ Next | ||
| 64 | - Tests through qpdf command line: copy pages from multiple PDFs | 64 | - Tests through qpdf command line: copy pages from multiple PDFs |
| 65 | starting with one PDF and also starting with empty. | 65 | starting with one PDF and also starting with empty. |
| 66 | 66 | ||
| 67 | - * (Hopefully) Provide an option to copy encryption parameters from | ||
| 68 | - another file. This would make it possible to decrypt a file, | ||
| 69 | - manually work with it, and then re-encrypt it using the original | ||
| 70 | - encryption parameters including a possibly unknown owner password. | 67 | + * qpdf commandline: provide an option to copy encryption parameters |
| 68 | + from another file, specifying file and password. Search for "Copy | ||
| 69 | + encryption parameters" in qpdf.test. | ||
| 71 | 70 | ||
| 72 | 71 | ||
| 73 | Soon | 72 | Soon |
include/qpdf/QPDFWriter.hh
| @@ -188,6 +188,11 @@ class QPDFWriter | @@ -188,6 +188,11 @@ class QPDFWriter | ||
| 188 | QPDF_DLL | 188 | QPDF_DLL |
| 189 | void setPreserveEncryption(bool); | 189 | void setPreserveEncryption(bool); |
| 190 | 190 | ||
| 191 | + // Copy encryption parameters from another QPDF object. If you | ||
| 192 | + // want to copy encryption from the object you are writing, call | ||
| 193 | + // setPreserveEncryption(true) instead. | ||
| 194 | + void copyEncryptionParameters(QPDF&); | ||
| 195 | + | ||
| 191 | // Set up for encrypted output. Disables stream prefiltering and | 196 | // Set up for encrypted output. Disables stream prefiltering and |
| 192 | // content normalization. Note that setting R2 encryption | 197 | // content normalization. Note that setting R2 encryption |
| 193 | // parameters sets the PDF version to at least 1.3, setting R3 | 198 | // parameters sets the PDF version to at least 1.3, setting R3 |
| @@ -269,7 +274,6 @@ class QPDFWriter | @@ -269,7 +274,6 @@ class QPDFWriter | ||
| 269 | int V, int R, int key_len, long P, | 274 | int V, int R, int key_len, long P, |
| 270 | std::string const& O, std::string const& U, | 275 | std::string const& O, std::string const& U, |
| 271 | std::string const& id1, std::string const& user_password); | 276 | std::string const& id1, std::string const& user_password); |
| 272 | - void copyEncryptionParameters(); | ||
| 273 | void setDataKey(int objid); | 277 | void setDataKey(int objid); |
| 274 | int openObject(int objid = 0); | 278 | int openObject(int objid = 0); |
| 275 | void closeObject(int objid); | 279 | void closeObject(int objid); |
libqpdf/QPDFWriter.cc
| @@ -389,10 +389,11 @@ QPDFWriter::setEncryptionParameters( | @@ -389,10 +389,11 @@ QPDFWriter::setEncryptionParameters( | ||
| 389 | } | 389 | } |
| 390 | 390 | ||
| 391 | void | 391 | void |
| 392 | -QPDFWriter::copyEncryptionParameters() | 392 | +QPDFWriter::copyEncryptionParameters(QPDF& qpdf) |
| 393 | { | 393 | { |
| 394 | + this->preserve_encryption = false; | ||
| 394 | generateID(); | 395 | generateID(); |
| 395 | - QPDFObjectHandle trailer = this->pdf.getTrailer(); | 396 | + QPDFObjectHandle trailer = qpdf.getTrailer(); |
| 396 | if (trailer.hasKey("/Encrypt")) | 397 | if (trailer.hasKey("/Encrypt")) |
| 397 | { | 398 | { |
| 398 | QPDFObjectHandle encrypt = trailer.getKey("/Encrypt"); | 399 | QPDFObjectHandle encrypt = trailer.getKey("/Encrypt"); |
| @@ -410,6 +411,8 @@ QPDFWriter::copyEncryptionParameters() | @@ -410,6 +411,8 @@ QPDFWriter::copyEncryptionParameters() | ||
| 410 | } | 411 | } |
| 411 | QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", | 412 | QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", |
| 412 | this->encrypt_metadata ? 0 : 1); | 413 | this->encrypt_metadata ? 0 : 1); |
| 414 | + this->id1 = | ||
| 415 | + trailer.getKey("/ID").getArrayItem(0).getStringValue(); | ||
| 413 | setEncryptionParametersInternal( | 416 | setEncryptionParametersInternal( |
| 414 | V, | 417 | V, |
| 415 | encrypt.getKey("/R").getIntValue(), | 418 | encrypt.getKey("/R").getIntValue(), |
| @@ -1625,7 +1628,7 @@ QPDFWriter::write() | @@ -1625,7 +1628,7 @@ QPDFWriter::write() | ||
| 1625 | 1628 | ||
| 1626 | if (preserve_encryption) | 1629 | if (preserve_encryption) |
| 1627 | { | 1630 | { |
| 1628 | - copyEncryptionParameters(); | 1631 | + copyEncryptionParameters(this->pdf); |
| 1629 | } | 1632 | } |
| 1630 | 1633 | ||
| 1631 | if (! this->forced_pdf_version.empty()) | 1634 | if (! this->forced_pdf_version.empty()) |
qpdf/qtest/qpdf.test
| @@ -1369,6 +1369,23 @@ $td->runtest("check output", | @@ -1369,6 +1369,23 @@ $td->runtest("check output", | ||
| 1369 | {$td->FILE => 'a.pdf'}, | 1369 | {$td->FILE => 'a.pdf'}, |
| 1370 | {$td->FILE => 'decrypted-crypt-filter.pdf'}); | 1370 | {$td->FILE => 'decrypted-crypt-filter.pdf'}); |
| 1371 | 1371 | ||
| 1372 | +# Copy encryption parameters | ||
| 1373 | +$n_tests += 3; | ||
| 1374 | +$td->runtest("create encrypted file", | ||
| 1375 | + {$td->COMMAND => | ||
| 1376 | + "qpdf --encrypt user owner 128 --use-aes=y --extract=n --" . | ||
| 1377 | + " minimal.pdf a.pdf"}, | ||
| 1378 | + {$td->STRING => "", $td->EXIT_STATUS => 0}); | ||
| 1379 | +$td->runtest("copy encryption parameters", | ||
| 1380 | + {$td->COMMAND => "test_driver 30 minimal.pdf a.pdf"}, | ||
| 1381 | + {$td->STRING => "test 30 done\n", $td->EXIT_STATUS => 0}, | ||
| 1382 | + $td->NORMALIZE_NEWLINES); | ||
| 1383 | +$td->runtest("checkout encryption", | ||
| 1384 | + {$td->COMMAND => "qpdf --show-encryption b.pdf --password=owner"}, | ||
| 1385 | + {$td->FILE => "copied-encryption.out", | ||
| 1386 | + $td->EXIT_STATUS => 0}, | ||
| 1387 | + $td->NORMALIZE_NEWLINES); | ||
| 1388 | + | ||
| 1372 | show_ntests(); | 1389 | show_ntests(); |
| 1373 | # ---------- | 1390 | # ---------- |
| 1374 | $td->notify("--- Content Preservation Tests ---"); | 1391 | $td->notify("--- Content Preservation Tests ---"); |
qpdf/qtest/qpdf/copied-encryption.out
0 → 100644
| 1 | +R = 4 | ||
| 2 | +P = -20 | ||
| 3 | +User password = user | ||
| 4 | +extract for accessibility: allowed | ||
| 5 | +extract for any purpose: not allowed | ||
| 6 | +print low resolution: allowed | ||
| 7 | +print high resolution: allowed | ||
| 8 | +modify document assembly: allowed | ||
| 9 | +modify forms: allowed | ||
| 10 | +modify annotations: allowed | ||
| 11 | +modify other: allowed | ||
| 12 | +modify anything: allowed |
qpdf/test_driver.cc
| @@ -1024,6 +1024,17 @@ void runtest(int n, char const* filename1, char const* filename2) | @@ -1024,6 +1024,17 @@ void runtest(int n, char const* filename1, char const* filename2) | ||
| 1024 | std::cout << "logic error: " << e.what() << std::endl; | 1024 | std::cout << "logic error: " << e.what() << std::endl; |
| 1025 | } | 1025 | } |
| 1026 | } | 1026 | } |
| 1027 | + else if (n == 30) | ||
| 1028 | + { | ||
| 1029 | + assert(filename2 != 0); | ||
| 1030 | + QPDF encrypted; | ||
| 1031 | + encrypted.processFile(filename2, "user"); | ||
| 1032 | + QPDFWriter w(pdf, "b.pdf"); | ||
| 1033 | + w.setStaticID(true); | ||
| 1034 | + w.setStreamDataMode(qpdf_s_preserve); | ||
| 1035 | + w.copyEncryptionParameters(encrypted); | ||
| 1036 | + w.write(); | ||
| 1037 | + } | ||
| 1027 | else | 1038 | else |
| 1028 | { | 1039 | { |
| 1029 | throw std::runtime_error(std::string("invalid test ") + | 1040 | throw std::runtime_error(std::string("invalid test ") + |