Commit c5ed1b8075f412b2e9cfd9cf01f41ba04d3af2bc

Authored by Jay Berkenbilt
1 parent 551dfbf6

Handle invalid encryption Length (fixes #333)

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 &quot;\n&quot;; @@ -4353,6 +4353,14 @@ print &quot;\n&quot;;
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-&gt;runtest(&quot;check file&#39;s validity&quot;, @@ -3534,6 +3534,11 @@ $td-&gt;runtest(&quot;check file&#39;s validity&quot;,
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