Commit 0575d77d77b0308535422ad2d3a1bf7c94baabb0

Authored by Jay Berkenbilt
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.
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.
@@ -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-&gt;runtest(&quot;check output&quot;, @@ -1369,6 +1369,23 @@ $td-&gt;runtest(&quot;check output&quot;,
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 ") +