Commit 46cb7392732451e7ba12a8e1a7d30c4cb1014ac0

Authored by m-holger
1 parent a78d18f7

Refactor `QPDFWriter` encryption logic to simplify pipeline writes and consolida…

…te encryption handling
include/qpdf/QPDFWriter.hh
... ... @@ -466,6 +466,7 @@ class QPDFWriter
466 466 QPDFWriter& write(std::integral auto val);
467 467 QPDFWriter& write_name(std::string const& str);
468 468 QPDFWriter& write_string(std::string const& str, bool force_binary = false);
  469 + QPDFWriter& write_encrypted(std::string_view str);
469 470  
470 471 template <typename... Args>
471 472 QPDFWriter& write_qdf(Args&&... args);
... ... @@ -569,7 +570,6 @@ class QPDFWriter
569 570 // is popped.
570 571  
571 572 void adjustAESStreamLength(size_t& length);
572   - PipelinePopper pushEncryptionFilter();
573 573 void pushMD5Pipeline(PipelinePopper&);
574 574 void computeDeterministicIDData();
575 575  
... ...
libqpdf/QPDFWriter.cc
... ... @@ -1098,32 +1098,27 @@ QPDFWriter::adjustAESStreamLength(size_t&amp; length)
1098 1098 }
1099 1099 }
1100 1100  
1101   -QPDFWriter::PipelinePopper
1102   -QPDFWriter::pushEncryptionFilter()
1103   -{
1104   - auto pp = m->pipeline_stack.popper();
1105   - if (m->encryption && !m->cur_data_key.empty()) {
1106   - Pipeline* p = nullptr;
1107   - if (m->encrypt_use_aes) {
1108   - p = new Pl_AES_PDF(
1109   - "aes stream encryption",
1110   - m->pipeline,
  1101 +QPDFWriter&
  1102 +QPDFWriter::write_encrypted(std::string_view str)
  1103 +{
  1104 + if (!(m->encryption && !m->cur_data_key.empty())) {
  1105 + write(str);
  1106 + } else if (m->encrypt_use_aes) {
  1107 + write(
  1108 + pl::pipe<Pl_AES_PDF>(
  1109 + str,
1111 1110 true,
1112 1111 QUtil::unsigned_char_pointer(m->cur_data_key),
1113   - m->cur_data_key.length());
1114   - } else {
1115   - p = new Pl_RC4(
1116   - "rc4 stream encryption",
1117   - m->pipeline,
  1112 + m->cur_data_key.length()));
  1113 + } else {
  1114 + write(
  1115 + pl::pipe<Pl_RC4>(
  1116 + str,
1118 1117 QUtil::unsigned_char_pointer(m->cur_data_key),
1119   - QIntC::to_int(m->cur_data_key.length()));
1120   - }
1121   - m->pipeline_stack.push(p);
  1118 + QIntC::to_int(m->cur_data_key.length())));
1122 1119 }
1123   - // Must call this unconditionally so we can call popPipelineStack to balance
1124   - // pushEncryptionFilter().
1125   - m->pipeline_stack.activate(pp);
1126   - return pp;
  1120 +
  1121 + return *this;
1127 1122 }
1128 1123  
1129 1124 void
... ... @@ -1659,18 +1654,9 @@ QPDFWriter::unparseObject(
1659 1654 adjustAESStreamLength(m->cur_stream_length);
1660 1655 unparseObject(stream_dict, 0, flags, m->cur_stream_length, compress_stream);
1661 1656 char last_char = stream_data.empty() ? '\0' : stream_data.back();
1662   - write("\nstream\n");
1663   - {
1664   - auto pp_enc = pushEncryptionFilter();
1665   - write(stream_data);
1666   - }
1667   -
1668   - if ((m->added_newline =
1669   - m->newline_before_endstream || (m->qdf_mode && last_char != '\n'))) {
1670   - write("\nendstream");
1671   - } else {
1672   - write("endstream");
1673   - }
  1657 + write("\nstream\n").write_encrypted(stream_data);
  1658 + m->added_newline = m->newline_before_endstream || (m->qdf_mode && last_char != '\n');
  1659 + write(m->added_newline ? "\nendstream" : "endstream");
1674 1660 } else if (tc == ::ot_string) {
1675 1661 std::string val;
1676 1662 if (m->encryption && !(flags & f_in_ostream) && !(flags & f_no_encryption) &&
... ... @@ -1842,18 +1828,11 @@ QPDFWriter::writeObjectStream(QPDFObjectHandle object)
1842 1828 unparseChild(extends, 1, f_in_ostream);
1843 1829 }
1844 1830 }
1845   - write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n");
  1831 + write_qdf("\n").write_no_qdf(" ").write(">>\nstream\n").write_encrypted(stream_buffer_pass2);
1846 1832 if (m->encryption) {
1847 1833 QTC::TC("qpdf", "QPDFWriter encrypt object stream");
1848 1834 }
1849   - {
1850   - auto pp_enc = pushEncryptionFilter();
1851   - write(stream_buffer_pass2);
1852   - }
1853   - if (m->newline_before_endstream) {
1854   - write("\n");
1855   - }
1856   - write("endstream");
  1835 + write(m->newline_before_endstream ? "\nendstream" : "endstream");
1857 1836 m->cur_data_key.clear();
1858 1837 closeObject(new_stream_id);
1859 1838 }
... ... @@ -2462,21 +2441,13 @@ QPDFWriter::writeHintStream(int hint_id)
2462 2441 }
2463 2442 adjustAESStreamLength(hlen);
2464 2443 write(" /Length ").write(hlen);
2465   - write(" >>\nstream\n");
  2444 + write(" >>\nstream\n").write_encrypted(hint_buffer);
2466 2445  
2467 2446 if (m->encryption) {
2468 2447 QTC::TC("qpdf", "QPDFWriter encrypted hint stream");
2469 2448 }
2470   - char last_char = hint_buffer.empty() ? '\0' : hint_buffer.back();
2471   - {
2472   - auto pp_enc = pushEncryptionFilter();
2473   - write(hint_buffer);
2474   - }
2475 2449  
2476   - if (last_char != '\n') {
2477   - write("\n");
2478   - }
2479   - write("endstream");
  2450 + write(hint_buffer.empty() || hint_buffer.back() != '\n' ? "\nendstream" : "endstream");
2480 2451 closeObject(hint_id);
2481 2452 }
2482 2453  
... ...
libqpdf/qpdf/Pipeline_private.hh
... ... @@ -173,6 +173,18 @@ namespace qpdf::pl
173 173 unsigned long id_{0};
174 174 bool pass_immediately_to_next{false};
175 175 };
  176 +
  177 + template <typename P, typename... Args>
  178 + std::string
  179 + pipe(std::string_view data, Args&&... args)
  180 + {
  181 + std::string result;
  182 + String s("", nullptr, result);
  183 + P pl("", &s, std::forward<Args>(args)...);
  184 + pl.write(reinterpret_cast<unsigned char const*>(data.data()), data.size());
  185 + pl.finish();
  186 + return result;
  187 + }
176 188 } // namespace qpdf::pl
177 189  
178 190 #endif // PIPELINE_PRIVATE_HH
... ...