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,7 +183,7 @@ truncate_password_V5(std::string const& password) | ||
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | static void | 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 | md5.digest(digest); | 188 | md5.digest(digest); |
| 189 | 189 | ||
| @@ -191,7 +191,7 @@ iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations) | @@ -191,7 +191,7 @@ iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations) | ||
| 191 | { | 191 | { |
| 192 | MD5 m; | 192 | MD5 m; |
| 193 | m.encodeDataIncrementally(reinterpret_cast<char*>(digest), | 193 | m.encodeDataIncrementally(reinterpret_cast<char*>(digest), |
| 194 | - sizeof(digest)); | 194 | + key_len); |
| 195 | m.digest(digest); | 195 | m.digest(digest); |
| 196 | } | 196 | } |
| 197 | } | 197 | } |
| @@ -437,7 +437,8 @@ QPDF::compute_encryption_key_from_password( | @@ -437,7 +437,8 @@ QPDF::compute_encryption_key_from_password( | ||
| 437 | md5.encodeDataIncrementally(bytes, 4); | 437 | md5.encodeDataIncrementally(bytes, 4); |
| 438 | } | 438 | } |
| 439 | MD5::Digest digest; | 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 | return std::string(reinterpret_cast<char*>(digest), | 442 | return std::string(reinterpret_cast<char*>(digest), |
| 442 | std::min(static_cast<int>(sizeof(digest)), | 443 | std::min(static_cast<int>(sizeof(digest)), |
| 443 | data.getLengthBytes())); | 444 | data.getLengthBytes())); |
| @@ -463,7 +464,8 @@ compute_O_rc4_key(std::string const& user_password, | @@ -463,7 +464,8 @@ compute_O_rc4_key(std::string const& user_password, | ||
| 463 | md5.encodeDataIncrementally( | 464 | md5.encodeDataIncrementally( |
| 464 | pad_or_truncate_password_V4(password).c_str(), key_bytes); | 465 | pad_or_truncate_password_V4(password).c_str(), key_bytes); |
| 465 | MD5::Digest digest; | 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 | memcpy(key, digest, OU_key_bytes_V4); | 469 | memcpy(key, digest, OU_key_bytes_V4); |
| 468 | } | 470 | } |
| 469 | 471 | ||
| @@ -933,6 +935,11 @@ QPDF::initializeEncryption() | @@ -933,6 +935,11 @@ QPDF::initializeEncryption() | ||
| 933 | if (encryption_dict.getKey("/Length").isInteger()) | 935 | if (encryption_dict.getKey("/Length").isInteger()) |
| 934 | { | 936 | { |
| 935 | Length = encryption_dict.getKey("/Length").getIntValue(); | 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 | if ((Length % 8) || (Length < 40) || (Length > 256)) | 943 | if ((Length % 8) || (Length < 40) || (Length > 256)) |
| 937 | { | 944 | { |
| 938 | throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), | 945 | throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(), |
qpdf/qtest/qpdf.test
| @@ -453,7 +453,7 @@ $td->runtest("check output", | @@ -453,7 +453,7 @@ $td->runtest("check output", | ||
| 453 | show_ntests(); | 453 | show_ntests(); |
| 454 | # ---------- | 454 | # ---------- |
| 455 | $td->notify("--- ID and Encryption Parameter Issues ---"); | 455 | $td->notify("--- ID and Encryption Parameter Issues ---"); |
| 456 | -$n_tests += 12; | 456 | +$n_tests += 13; |
| 457 | 457 | ||
| 458 | # Encrypt files whose /ID strings are other than 32 bytes long (bug | 458 | # Encrypt files whose /ID strings are other than 32 bytes long (bug |
| 459 | # 2991412). Also linearize these files, which was reported in a | 459 | # 2991412). Also linearize these files, which was reported in a |
| @@ -509,6 +509,19 @@ $td->runtest("short /O or /U", | @@ -509,6 +509,19 @@ $td->runtest("short /O or /U", | ||
| 509 | $td->EXIT_STATUS => 0}, | 509 | $td->EXIT_STATUS => 0}, |
| 510 | $td->NORMALIZE_NEWLINES); | 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 | show_ntests(); | 525 | show_ntests(); |
| 513 | # ---------- | 526 | # ---------- |
| 514 | $td->notify("--- Min/force version ---"); | 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