aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2019-11-09 17:29:52 +0100
committerJay Berkenbilt <ejb@ql.org>2019-11-09 18:33:15 +0100
commit5508f74603d7a816ab67456939bd3ee57676f842 (patch)
treee918fef9f6da33f7dc0b8a3c8e69369e29dc0a70
parentb997fa5343ea42054338b9143b3205cb6c164e6e (diff)
downloadqpdf-5508f74603d7a816ab67456939bd3ee57676f842.tar.zst
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.
-rw-r--r--ChangeLog11
-rw-r--r--libqpdf/QPDFPageObjectHelper.cc1
-rw-r--r--libqpdf/QPDFWriter.cc2
-rw-r--r--libqpdf/QPDF_encryption.cc4
-rw-r--r--qpdf/qtest/qpdf.test26
-rw-r--r--qpdf/qtest/qpdf/copied-positive-P.pdfbin0 -> 1480 bytes
-rw-r--r--qpdf/qtest/qpdf/decrypted-positive-P.pdfbin0 -> 865 bytes
-rw-r--r--qpdf/qtest/qpdf/encrypted-positive-P.pdf39
8 files changed, 80 insertions, 3 deletions
diff --git a/ChangeLog b/ChangeLog
index d2013565..3e3d7a4a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2019-11-09 Jay Berkenbilt <ejb@ql.org>
+
+ * When reading /P from the encryption dictionary, use static_cast
+ instead of QIntC to convert the value to a signed integer. The
+ value of /P is a bit field, and PDF files have been found in the
+ wild where /P is represented as an unsigned integer even though
+ the spec states that it is a signed 32-bit value. By using
+ static_cast, we allow qpdf to compensate for writers that
+ incorrectly represent the correct bit field as an unsigned value.
+ Fixes #382.
+
2019-11-05 Jay Berkenbilt <ejb@ql.org>
* Add support for pluggable crypto providers, enabling multiple
diff --git a/libqpdf/QPDFPageObjectHelper.cc b/libqpdf/QPDFPageObjectHelper.cc
index 8ecd0144..d588c322 100644
--- a/libqpdf/QPDFPageObjectHelper.cc
+++ b/libqpdf/QPDFPageObjectHelper.cc
@@ -661,6 +661,7 @@ QPDFPageObjectHelper::getFormXObjectForPage(bool handle_transformations)
return result;
}
+// ABI: name should be std:string const&
std::string
QPDFPageObjectHelper::placeFormXObject(
QPDFObjectHandle fo, std::string name,
diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc
index a31f5da9..7b769ae4 100644
--- a/libqpdf/QPDFWriter.cc
+++ b/libqpdf/QPDFWriter.cc
@@ -762,7 +762,7 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf)
V,
encrypt.getKey("/R").getIntValueAsInt(),
key_len,
- encrypt.getKey("/P").getIntValueAsInt(),
+ static_cast<int>(encrypt.getKey("/P").getIntValue()),
encrypt.getKey("/O").getStringValue(),
encrypt.getKey("/U").getStringValue(),
OE,
diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc
index 5a6735ee..9880face 100644
--- a/libqpdf/QPDF_encryption.cc
+++ b/libqpdf/QPDF_encryption.cc
@@ -877,7 +877,7 @@ QPDF::initializeEncryption()
int R = encryption_dict.getKey("/R").getIntValueAsInt();
std::string O = encryption_dict.getKey("/O").getStringValue();
std::string U = encryption_dict.getKey("/U").getStringValue();
- int P = encryption_dict.getKey("/P").getIntValueAsInt();
+ int P = static_cast<int>(encryption_dict.getKey("/P").getIntValue());
// If supporting new encryption R/V values, remember to update
// error message inside this if statement.
@@ -1448,7 +1448,7 @@ QPDF::isEncrypted(int& R, int& P, int& V,
QPDFObjectHandle Pkey = encrypt.getKey("/P");
QPDFObjectHandle Rkey = encrypt.getKey("/R");
QPDFObjectHandle Vkey = encrypt.getKey("/V");
- P = Pkey.getIntValueAsInt();
+ P = static_cast<int>(Pkey.getIntValue());
R = Rkey.getIntValueAsInt();
V = Vkey.getIntValueAsInt();
stream_method = this->m->encp->cf_stream;
diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test
index 1df8291e..7ea329c2 100644
--- a/qpdf/qtest/qpdf.test
+++ b/qpdf/qtest/qpdf.test
@@ -721,6 +721,32 @@ foreach my $d (@bug_tests)
}
show_ntests();
# ----------
+$td->notify("--- Positive /P in encryption dictionary ---");
+$n_tests += 4;
+
+# Files have been seen where /P in the encryption dictionary was an
+# unsigned rather than a signed integer. To create
+# encrypted-positive-P.pdf, I temporarily modified QPDFWriter.cc to
+# introduce this error.
+
+$td->runtest("decrypt positive P",
+ {$td->COMMAND =>
+ "qpdf --decrypt --static-id encrypted-positive-P.pdf a.pdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0});
+$td->runtest("check output",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "decrypted-positive-P.pdf"});
+$td->runtest("copy encryption positive P",
+ {$td->COMMAND =>
+ "qpdf --static-id --static-aes-iv" .
+ " encrypted-positive-P.pdf a.pdf"},
+ {$td->STRING => "", $td->EXIT_STATUS => 0});
+$td->runtest("check output",
+ {$td->FILE => "a.pdf"},
+ {$td->FILE => "copied-positive-P.pdf"});
+
+show_ntests();
+# ----------
$td->notify("--- Library version ---");
$n_tests += 3;
diff --git a/qpdf/qtest/qpdf/copied-positive-P.pdf b/qpdf/qtest/qpdf/copied-positive-P.pdf
new file mode 100644
index 00000000..c13d9040
--- /dev/null
+++ b/qpdf/qtest/qpdf/copied-positive-P.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/decrypted-positive-P.pdf b/qpdf/qtest/qpdf/decrypted-positive-P.pdf
new file mode 100644
index 00000000..964a7c2c
--- /dev/null
+++ b/qpdf/qtest/qpdf/decrypted-positive-P.pdf
Binary files differ
diff --git a/qpdf/qtest/qpdf/encrypted-positive-P.pdf b/qpdf/qtest/qpdf/encrypted-positive-P.pdf
new file mode 100644
index 00000000..e42772c4
--- /dev/null
+++ b/qpdf/qtest/qpdf/encrypted-positive-P.pdf
@@ -0,0 +1,39 @@
+%PDF-1.7
+%
+1 0 obj
+<< /Extensions << /ADBE << /BaseVersion /1.7 /ExtensionLevel 8 >> >> /Pages 2 0 R /Type /Catalog >>
+endobj
+2 0 obj
+<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >>
+endobj
+3 0 obj
+<< /Contents 4 0 R /MediaBox [ 0 0 612 792 ] /Parent 2 0 R /Resources << /Font << /F1 5 0 R >> /ProcSet 6 0 R >> /Type /Page >>
+endobj
+4 0 obj
+<< /Length 80 /Filter /FlateDecode >>
+stream
+P옃Пu@BL$:je^~[j};C5 \̉.(doF G*C5{kxQendstream
+endobj
+5 0 obj
+<< /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font >>
+endobj
+6 0 obj
+[ /PDF /Text ]
+endobj
+7 0 obj
+<< /CF << /StdCF << /AuthEvent /DocOpen /CFM /AESV3 /Length 32 >> >> /Filter /Standard /Length 256 /O <1b7cc50a8bd7d4220be2b990df3a3f82bf54a9596ea0d3f0fe308a62a496d7f2853cb41fd7e8f2adfee08f045f4579b0> /OE <968299e6df135ce14da318e851103f18c263082e6ae1380fcbf26e910cef551b> /P 4294967292 /Perms <afb6ec9c6b9c3e8c2fbdcb9c4f391a1d> /R 6 /StmF /StdCF /StrF /StdCF /U <16d19f98d4d5144f6e68ec10294e3789d8eab6a8eb6de68231d749c198904a004b8a16d4ceb3553204d16feae9d1b819> /UE <80044f0bc684a6675f129a0cb8a6ed6efa33d890a6195963a75e8b3876ded541> /V 5 >>
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000015 00000 n
+0000000130 00000 n
+0000000189 00000 n
+0000000332 00000 n
+0000000482 00000 n
+0000000589 00000 n
+0000000619 00000 n
+trailer << /Root 1 0 R /Size 8 /ID [<7aed6705f40de848cd4f3ea12c672f33><7aed6705f40de848cd4f3ea12c672f33>] /Encrypt 7 0 R >>
+startxref
+1174
+%%EOF