Commit 5508f74603d7a816ab67456939bd3ee57676f842
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.
Showing
8 changed files
with
80 additions
and
3 deletions
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& qpdf) | @@ -762,7 +762,7 @@ QPDFWriter::copyEncryptionParameters(QPDF& 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& R, int& P, int& V, | @@ -1448,7 +1448,7 @@ QPDF::isEncrypted(int& R, int& P, int& 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