Commit 5508f74603d7a816ab67456939bd3ee57676f842

Authored by Jay Berkenbilt
1 parent b997fa53

Allow /P in encryption dictionary to be positive (fixes #382)

Even though this is disallowed by the spec, files like this have been
encountered in the wild.
ChangeLog
  1 +2019-11-09 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * When reading /P from the encryption dictionary, use static_cast
  4 + instead of QIntC to convert the value to a signed integer. The
  5 + value of /P is a bit field, and PDF files have been found in the
  6 + wild where /P is represented as an unsigned integer even though
  7 + the spec states that it is a signed 32-bit value. By using
  8 + static_cast, we allow qpdf to compensate for writers that
  9 + incorrectly represent the correct bit field as an unsigned value.
  10 + Fixes #382.
  11 +
1 2019-11-05 Jay Berkenbilt <ejb@ql.org> 12 2019-11-05 Jay Berkenbilt <ejb@ql.org>
2 13
3 * Add support for pluggable crypto providers, enabling multiple 14 * Add support for pluggable crypto providers, enabling multiple
libqpdf/QPDFPageObjectHelper.cc
@@ -661,6 +661,7 @@ QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations) @@ -661,6 +661,7 @@ QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations)
661 return result; 661 return result;
662 } 662 }
663 663
  664 +// ABI: name should be std:string const&
664 std::string 665 std::string
665 QPDFPageObjectHelper::placeFormXObject( 666 QPDFPageObjectHelper::placeFormXObject(
666 QPDFObjectHandle fo, std::string name, 667 QPDFObjectHandle fo, std::string name,
libqpdf/QPDFWriter.cc
@@ -762,7 +762,7 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf) @@ -762,7 +762,7 @@ QPDFWriter::copyEncryptionParameters(QPDF&amp; qpdf)
762 V, 762 V,
763 encrypt.getKey("/R").getIntValueAsInt(), 763 encrypt.getKey("/R").getIntValueAsInt(),
764 key_len, 764 key_len,
765 - encrypt.getKey("/P").getIntValueAsInt(), 765 + static_cast<int>(encrypt.getKey("/P").getIntValue()),
766 encrypt.getKey("/O").getStringValue(), 766 encrypt.getKey("/O").getStringValue(),
767 encrypt.getKey("/U").getStringValue(), 767 encrypt.getKey("/U").getStringValue(),
768 OE, 768 OE,
libqpdf/QPDF_encryption.cc
@@ -877,7 +877,7 @@ QPDF::initializeEncryption() @@ -877,7 +877,7 @@ QPDF::initializeEncryption()
877 int R = encryption_dict.getKey("/R").getIntValueAsInt(); 877 int R = encryption_dict.getKey("/R").getIntValueAsInt();
878 std::string O = encryption_dict.getKey("/O").getStringValue(); 878 std::string O = encryption_dict.getKey("/O").getStringValue();
879 std::string U = encryption_dict.getKey("/U").getStringValue(); 879 std::string U = encryption_dict.getKey("/U").getStringValue();
880 - int P = encryption_dict.getKey("/P").getIntValueAsInt(); 880 + int P = static_cast<int>(encryption_dict.getKey("/P").getIntValue());
881 881
882 // If supporting new encryption R/V values, remember to update 882 // If supporting new encryption R/V values, remember to update
883 // error message inside this if statement. 883 // error message inside this if statement.
@@ -1448,7 +1448,7 @@ QPDF::isEncrypted(int&amp; R, int&amp; P, int&amp; V, @@ -1448,7 +1448,7 @@ QPDF::isEncrypted(int&amp; R, int&amp; P, int&amp; V,
1448 QPDFObjectHandle Pkey = encrypt.getKey("/P"); 1448 QPDFObjectHandle Pkey = encrypt.getKey("/P");
1449 QPDFObjectHandle Rkey = encrypt.getKey("/R"); 1449 QPDFObjectHandle Rkey = encrypt.getKey("/R");
1450 QPDFObjectHandle Vkey = encrypt.getKey("/V"); 1450 QPDFObjectHandle Vkey = encrypt.getKey("/V");
1451 - P = Pkey.getIntValueAsInt(); 1451 + P = static_cast<int>(Pkey.getIntValue());
1452 R = Rkey.getIntValueAsInt(); 1452 R = Rkey.getIntValueAsInt();
1453 V = Vkey.getIntValueAsInt(); 1453 V = Vkey.getIntValueAsInt();
1454 stream_method = this->m->encp->cf_stream; 1454 stream_method = this->m->encp->cf_stream;
qpdf/qtest/qpdf.test
@@ -721,6 +721,32 @@ foreach my $d (@bug_tests) @@ -721,6 +721,32 @@ foreach my $d (@bug_tests)
721 } 721 }
722 show_ntests(); 722 show_ntests();
723 # ---------- 723 # ----------
  724 +$td->notify("--- Positive /P in encryption dictionary ---");
  725 +$n_tests += 4;
  726 +
  727 +# Files have been seen where /P in the encryption dictionary was an
  728 +# unsigned rather than a signed integer. To create
  729 +# encrypted-positive-P.pdf, I temporarily modified QPDFWriter.cc to
  730 +# introduce this error.
  731 +
  732 +$td->runtest("decrypt positive P",
  733 + {$td->COMMAND =>
  734 + "qpdf --decrypt --static-id encrypted-positive-P.pdf a.pdf"},
  735 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  736 +$td->runtest("check output",
  737 + {$td->FILE => "a.pdf"},
  738 + {$td->FILE => "decrypted-positive-P.pdf"});
  739 +$td->runtest("copy encryption positive P",
  740 + {$td->COMMAND =>
  741 + "qpdf --static-id --static-aes-iv" .
  742 + " encrypted-positive-P.pdf a.pdf"},
  743 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  744 +$td->runtest("check output",
  745 + {$td->FILE => "a.pdf"},
  746 + {$td->FILE => "copied-positive-P.pdf"});
  747 +
  748 +show_ntests();
  749 +# ----------
724 $td->notify("--- Library version ---"); 750 $td->notify("--- Library version ---");
725 $n_tests += 3; 751 $n_tests += 3;
726 752
qpdf/qtest/qpdf/copied-positive-P.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/decrypted-positive-P.pdf 0 โ†’ 100644
No preview for this file type
qpdf/qtest/qpdf/encrypted-positive-P.pdf 0 โ†’ 100644
No preview for this file type