Commit c5ed1b8075f412b2e9cfd9cf01f41ba04d3af2bc
1 parent
551dfbf6
Handle invalid encryption Length (fixes #333)
Showing
6 changed files
with
102 additions
and
14 deletions
ChangeLog
| 1 | 2019-06-22 Jay Berkenbilt <ejb@ql.org> | 1 | 2019-06-22 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * Handle encrypted files with missing or invalid /Length entries | ||
| 4 | + in the encryption dictionary. | ||
| 5 | + | ||
| 3 | * QPDFWriter: allow calling set*EncryptionParameters before | 6 | * QPDFWriter: allow calling set*EncryptionParameters before |
| 4 | calling setFilename. Fixes #336. | 7 | calling setFilename. Fixes #336. |
| 5 | 8 |
libqpdf/QPDF_encryption.cc
| @@ -935,22 +935,38 @@ QPDF::initializeEncryption() | @@ -935,22 +935,38 @@ QPDF::initializeEncryption() | ||
| 935 | pad_short_parameter(Perms, Perms_key_bytes_V5); | 935 | pad_short_parameter(Perms, Perms_key_bytes_V5); |
| 936 | } | 936 | } |
| 937 | 937 | ||
| 938 | - int Length = 40; | ||
| 939 | - if (encryption_dict.getKey("/Length").isInteger()) | 938 | + int Length = 0; |
| 939 | + if (V <= 1) | ||
| 940 | { | 940 | { |
| 941 | - Length = encryption_dict.getKey("/Length").getIntValueAsInt(); | ||
| 942 | - if (R < 3) | 941 | + Length = 40; |
| 942 | + } | ||
| 943 | + else if (V == 4) | ||
| 944 | + { | ||
| 945 | + Length = 128; | ||
| 946 | + } | ||
| 947 | + else if (V == 5) | ||
| 948 | + { | ||
| 949 | + Length = 256; | ||
| 950 | + } | ||
| 951 | + else | ||
| 952 | + { | ||
| 953 | + if (encryption_dict.getKey("/Length").isInteger()) | ||
| 954 | + { | ||
| 955 | + Length = encryption_dict.getKey("/Length").getIntValueAsInt(); | ||
| 956 | + if ((Length % 8) || (Length < 40) || (Length > 128)) | ||
| 957 | + { | ||
| 958 | + Length = 0; | ||
| 959 | + } | ||
| 960 | + } | ||
| 961 | + if (Length == 0) | ||
| 943 | { | 962 | { |
| 944 | - // Force Length to 40 regardless of what the file says. | ||
| 945 | - Length = 40; | 963 | + Length = 128; |
| 946 | } | 964 | } |
| 947 | - if ((Length % 8) || (Length < 40) || (Length > 256)) | ||
| 948 | - { | ||
| 949 | - throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), | ||
| 950 | - "encryption dictionary", | ||
| 951 | - this->m->file->getLastOffset(), | ||
| 952 | - "invalid /Length value in encryption dictionary"); | ||
| 953 | - } | 965 | + } |
| 966 | + if (Length == 0) | ||
| 967 | + { | ||
| 968 | + // Still no Length? Just take a guess. | ||
| 969 | + Length = 128; | ||
| 954 | } | 970 | } |
| 955 | 971 | ||
| 956 | this->m->encp->encrypt_metadata = true; | 972 | this->m->encp->encrypt_metadata = true; |
manual/qpdf-manual.xml
| @@ -4353,6 +4353,14 @@ print "\n"; | @@ -4353,6 +4353,14 @@ print "\n"; | ||
| 4353 | segmentation fault. | 4353 | segmentation fault. |
| 4354 | </para> | 4354 | </para> |
| 4355 | </listitem> | 4355 | </listitem> |
| 4356 | + <listitem> | ||
| 4357 | + <para> | ||
| 4358 | + When reading encrypted files, follow the spec more closely | ||
| 4359 | + regarding encryption key length. This allows qpdf to open | ||
| 4360 | + encrypted files in most cases when they have invalid or | ||
| 4361 | + missing /Length keys in the encryption dictionary. | ||
| 4362 | + </para> | ||
| 4363 | + </listitem> | ||
| 4356 | </itemizedlist> | 4364 | </itemizedlist> |
| 4357 | </listitem> | 4365 | </listitem> |
| 4358 | <listitem> | 4366 | <listitem> |
qpdf/qtest/qpdf.test
| @@ -3523,7 +3523,7 @@ foreach my $d (@enc_key) | @@ -3523,7 +3523,7 @@ foreach my $d (@enc_key) | ||
| 3523 | } | 3523 | } |
| 3524 | 3524 | ||
| 3525 | # Miscellaneous encryption tests | 3525 | # Miscellaneous encryption tests |
| 3526 | -$n_tests += 2; | 3526 | +$n_tests += 3; |
| 3527 | 3527 | ||
| 3528 | $td->runtest("set encryption before set filename", | 3528 | $td->runtest("set encryption before set filename", |
| 3529 | {$td->COMMAND => "test_driver 63 minimal.pdf"}, | 3529 | {$td->COMMAND => "test_driver 63 minimal.pdf"}, |
| @@ -3534,6 +3534,11 @@ $td->runtest("check file's validity", | @@ -3534,6 +3534,11 @@ $td->runtest("check file's validity", | ||
| 3534 | {$td->FILE => "encrypt-before-filename.out", | 3534 | {$td->FILE => "encrypt-before-filename.out", |
| 3535 | $td->EXIT_STATUS => 0}, | 3535 | $td->EXIT_STATUS => 0}, |
| 3536 | $td->NORMALIZE_NEWLINES); | 3536 | $td->NORMALIZE_NEWLINES); |
| 3537 | +$td->runtest("handle missing/invalid Length", | ||
| 3538 | + {$td->COMMAND => "qpdf --check bad-encryption-length.pdf"}, | ||
| 3539 | + {$td->FILE => "bad-encryption-length.out", | ||
| 3540 | + $td->EXIT_STATUS => 0}, | ||
| 3541 | + $td->NORMALIZE_NEWLINES); | ||
| 3537 | 3542 | ||
| 3538 | show_ntests(); | 3543 | show_ntests(); |
| 3539 | # ---------- | 3544 | # ---------- |
qpdf/qtest/qpdf/bad-encryption-length.out
0 → 100644
| 1 | +checking bad-encryption-length.pdf | ||
| 2 | +PDF Version: 1.4 | ||
| 3 | +R = 3 | ||
| 4 | +P = -4 | ||
| 5 | +User password = | ||
| 6 | +extract for accessibility: allowed | ||
| 7 | +extract for any purpose: allowed | ||
| 8 | +print low resolution: allowed | ||
| 9 | +print high resolution: allowed | ||
| 10 | +modify document assembly: allowed | ||
| 11 | +modify forms: allowed | ||
| 12 | +modify annotations: allowed | ||
| 13 | +modify other: allowed | ||
| 14 | +modify anything: allowed | ||
| 15 | +File is not linearized | ||
| 16 | +No syntax or stream encoding errors found; the file may still contain | ||
| 17 | +errors that qpdf cannot detect |
qpdf/qtest/qpdf/bad-encryption-length.pdf
0 → 100644
No preview for this file type