Commit 3aad28aed0726dd1147bfabbd2deb2e575aa80bb
1 parent
c543c1e4
Bug fix: honor encryption key length with R=3 (fixes #212)
Showing
4 changed files
with
82 additions
and
5 deletions
libqpdf/QPDF_encryption.cc
| ... | ... | @@ -183,7 +183,7 @@ truncate_password_V5(std::string const& password) |
| 183 | 183 | } |
| 184 | 184 | |
| 185 | 185 | static void |
| 186 | -iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations) | |
| 186 | +iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations, int key_len) | |
| 187 | 187 | { |
| 188 | 188 | md5.digest(digest); |
| 189 | 189 | |
| ... | ... | @@ -191,7 +191,7 @@ iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations) |
| 191 | 191 | { |
| 192 | 192 | MD5 m; |
| 193 | 193 | m.encodeDataIncrementally(reinterpret_cast<char*>(digest), |
| 194 | - sizeof(digest)); | |
| 194 | + key_len); | |
| 195 | 195 | m.digest(digest); |
| 196 | 196 | } |
| 197 | 197 | } |
| ... | ... | @@ -437,7 +437,8 @@ QPDF::compute_encryption_key_from_password( |
| 437 | 437 | md5.encodeDataIncrementally(bytes, 4); |
| 438 | 438 | } |
| 439 | 439 | MD5::Digest digest; |
| 440 | - iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0)); | |
| 440 | + iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0), | |
| 441 | + data.getLengthBytes()); | |
| 441 | 442 | return std::string(reinterpret_cast<char*>(digest), |
| 442 | 443 | std::min(static_cast<int>(sizeof(digest)), |
| 443 | 444 | data.getLengthBytes())); |
| ... | ... | @@ -463,7 +464,8 @@ compute_O_rc4_key(std::string const& user_password, |
| 463 | 464 | md5.encodeDataIncrementally( |
| 464 | 465 | pad_or_truncate_password_V4(password).c_str(), key_bytes); |
| 465 | 466 | MD5::Digest digest; |
| 466 | - iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0)); | |
| 467 | + iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0), | |
| 468 | + data.getLengthBytes()); | |
| 467 | 469 | memcpy(key, digest, OU_key_bytes_V4); |
| 468 | 470 | } |
| 469 | 471 | |
| ... | ... | @@ -933,6 +935,11 @@ QPDF::initializeEncryption() |
| 933 | 935 | if (encryption_dict.getKey("/Length").isInteger()) |
| 934 | 936 | { |
| 935 | 937 | Length = encryption_dict.getKey("/Length").getIntValue(); |
| 938 | + if (R < 3) | |
| 939 | + { | |
| 940 | + // Force Length to 40 regardless of what the file says. | |
| 941 | + Length = 40; | |
| 942 | + } | |
| 936 | 943 | if ((Length % 8) || (Length < 40) || (Length > 256)) |
| 937 | 944 | { |
| 938 | 945 | throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), | ... | ... |
qpdf/qtest/qpdf.test
| ... | ... | @@ -453,7 +453,7 @@ $td->runtest("check output", |
| 453 | 453 | show_ntests(); |
| 454 | 454 | # ---------- |
| 455 | 455 | $td->notify("--- ID and Encryption Parameter Issues ---"); |
| 456 | -$n_tests += 12; | |
| 456 | +$n_tests += 13; | |
| 457 | 457 | |
| 458 | 458 | # Encrypt files whose /ID strings are other than 32 bytes long (bug |
| 459 | 459 | # 2991412). Also linearize these files, which was reported in a |
| ... | ... | @@ -509,6 +509,19 @@ $td->runtest("short /O or /U", |
| 509 | 509 | $td->EXIT_STATUS => 0}, |
| 510 | 510 | $td->NORMALIZE_NEWLINES); |
| 511 | 511 | |
| 512 | +# A file was sent to me privately as part of issue 212. This file was | |
| 513 | +# encrypted and had /R=3 and /V=1 and was using a 40-bit key. qpdf was | |
| 514 | +# failing to work properly on files with /R=3 and 40-bit keys. The | |
| 515 | +# test file is not this private file, but the encryption parameters | |
| 516 | +# were copied from it. Like the bug file, qpdf < 8.1 can't decrypt it. | |
| 517 | +$td->runtest("/R 3 with 40-bit key", | |
| 518 | + {$td->COMMAND => | |
| 519 | + "qpdf --password=623 --check --show-encryption-key" . | |
| 520 | + " encrypted-40-bit-R3.pdf"}, | |
| 521 | + {$td->FILE => "encrypted-40-bit-R3.out", | |
| 522 | + $td->EXIT_STATUS => 0}, | |
| 523 | + $td->NORMALIZE_NEWLINES); | |
| 524 | + | |
| 512 | 525 | show_ntests(); |
| 513 | 526 | # ---------- |
| 514 | 527 | $td->notify("--- Min/force version ---"); | ... | ... |
qpdf/qtest/qpdf/encrypted-40-bit-R3.out
0 → 100644
| 1 | +checking encrypted-40-bit-R3.pdf | |
| 2 | +PDF Version: 1.4 | |
| 3 | +R = 3 | |
| 4 | +P = -12 | |
| 5 | +User password = 623 | |
| 6 | +Encryption key = e390e220da | |
| 7 | +extract for accessibility: allowed | |
| 8 | +extract for any purpose: allowed | |
| 9 | +print low resolution: allowed | |
| 10 | +print high resolution: allowed | |
| 11 | +modify document assembly: allowed | |
| 12 | +modify forms: allowed | |
| 13 | +modify annotations: allowed | |
| 14 | +modify other: not allowed | |
| 15 | +modify anything: not allowed | |
| 16 | +File is not linearized | |
| 17 | +No syntax or stream encoding errors found; the file may still contain | |
| 18 | +errors that qpdf cannot detect | ... | ... |
qpdf/qtest/qpdf/encrypted-40-bit-R3.pdf
0 → 100644
No preview for this file type