Commit 11266b14f6e18a08e2c25cc09c1a8641c67fc683
1 parent
8417be17
Refactor `QPDF_encryption` to simplify random string generation using `util::ran…
…dom_string`, improve readability of salts and keys handling, and streamline `compute_Perms_value_V5_clear` logic by returning strings directly.
Showing
4 changed files
with
36 additions
and
36 deletions
include/qpdf/QPDF.hh
| @@ -552,8 +552,7 @@ class QPDF | @@ -552,8 +552,7 @@ class QPDF | ||
| 552 | bool check_owner_password_V4( | 552 | bool check_owner_password_V4( |
| 553 | std::string& user_password, std::string const& owner_password) const; | 553 | std::string& user_password, std::string const& owner_password) const; |
| 554 | bool check_owner_password_V5(std::string const& owner_passworda) const; | 554 | bool check_owner_password_V5(std::string const& owner_passworda) const; |
| 555 | - void | ||
| 556 | - compute_Perms_value_V5_clear(std::string const& encryption_key, unsigned char k[16]) const; | 555 | + std::string compute_Perms_value_V5_clear() const; |
| 557 | void compute_O_rc4_key( | 556 | void compute_O_rc4_key( |
| 558 | std::string const& user_password, | 557 | std::string const& user_password, |
| 559 | std::string const& owner_password, | 558 | std::string const& owner_password, |
libqpdf/QPDF_encryption.cc
| @@ -16,10 +16,13 @@ | @@ -16,10 +16,13 @@ | ||
| 16 | #include <qpdf/QTC.hh> | 16 | #include <qpdf/QTC.hh> |
| 17 | #include <qpdf/QUtil.hh> | 17 | #include <qpdf/QUtil.hh> |
| 18 | #include <qpdf/RC4.hh> | 18 | #include <qpdf/RC4.hh> |
| 19 | +#include <qpdf/Util.hh> | ||
| 19 | 20 | ||
| 20 | #include <algorithm> | 21 | #include <algorithm> |
| 21 | #include <cstring> | 22 | #include <cstring> |
| 22 | 23 | ||
| 24 | +using namespace qpdf; | ||
| 25 | + | ||
| 23 | static unsigned char const padding_string[] = { | 26 | static unsigned char const padding_string[] = { |
| 24 | 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, | 27 | 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, |
| 25 | 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a}; | 28 | 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a}; |
| @@ -617,12 +620,10 @@ QPDF::EncryptionData::compute_U_UE_value_V5( | @@ -617,12 +620,10 @@ QPDF::EncryptionData::compute_U_UE_value_V5( | ||
| 617 | std::string& out_UE) const | 620 | std::string& out_UE) const |
| 618 | { | 621 | { |
| 619 | // Algorithm 3.8 from the PDF 1.7 extension level 3 | 622 | // Algorithm 3.8 from the PDF 1.7 extension level 3 |
| 620 | - char k[16]; | ||
| 621 | - QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(k), sizeof(k)); | ||
| 622 | - std::string validation_salt(k, 8); | ||
| 623 | - std::string key_salt(k + 8, 8); | ||
| 624 | - out_U = hash_V5(user_password, validation_salt, "") + validation_salt + key_salt; | ||
| 625 | - std::string intermediate_key = hash_V5(user_password, key_salt, ""); | 623 | + auto validation_salt = util::random_string(8); |
| 624 | + auto key_salt = util::random_string(8); | ||
| 625 | + out_U = hash_V5(user_password, validation_salt, "").append(validation_salt).append(key_salt); | ||
| 626 | + auto intermediate_key = hash_V5(user_password, key_salt, ""); | ||
| 626 | out_UE = process_with_aes(intermediate_key, true, encryption_key); | 627 | out_UE = process_with_aes(intermediate_key, true, encryption_key); |
| 627 | } | 628 | } |
| 628 | 629 | ||
| @@ -635,41 +636,35 @@ QPDF::EncryptionData::compute_O_OE_value_V5( | @@ -635,41 +636,35 @@ QPDF::EncryptionData::compute_O_OE_value_V5( | ||
| 635 | std::string& out_OE) const | 636 | std::string& out_OE) const |
| 636 | { | 637 | { |
| 637 | // Algorithm 3.9 from the PDF 1.7 extension level 3 | 638 | // Algorithm 3.9 from the PDF 1.7 extension level 3 |
| 638 | - char k[16]; | ||
| 639 | - QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(k), sizeof(k)); | ||
| 640 | - std::string validation_salt(k, 8); | ||
| 641 | - std::string key_salt(k + 8, 8); | 639 | + auto validation_salt = util::random_string(8); |
| 640 | + auto key_salt = util::random_string(8); | ||
| 642 | out_O = hash_V5(owner_password, validation_salt, in_U) + validation_salt + key_salt; | 641 | out_O = hash_V5(owner_password, validation_salt, in_U) + validation_salt + key_salt; |
| 643 | std::string intermediate_key = hash_V5(owner_password, key_salt, in_U); | 642 | std::string intermediate_key = hash_V5(owner_password, key_salt, in_U); |
| 644 | out_OE = process_with_aes(intermediate_key, true, encryption_key); | 643 | out_OE = process_with_aes(intermediate_key, true, encryption_key); |
| 645 | } | 644 | } |
| 646 | 645 | ||
| 647 | -void | ||
| 648 | -QPDF::EncryptionData::compute_Perms_value_V5_clear( | ||
| 649 | - std::string const& encryption_key, unsigned char k[16]) const | 646 | +std::string |
| 647 | +QPDF::EncryptionData::compute_Perms_value_V5_clear() const | ||
| 650 | { | 648 | { |
| 651 | // From algorithm 3.10 from the PDF 1.7 extension level 3 | 649 | // From algorithm 3.10 from the PDF 1.7 extension level 3 |
| 652 | - unsigned long long extended_perms = | ||
| 653 | - 0xffffffff00000000LL | static_cast<unsigned long long>(getP()); | ||
| 654 | - for (int i = 0; i < 8; ++i) { | ||
| 655 | - k[i] = static_cast<unsigned char>(extended_perms & 0xff); | ||
| 656 | - extended_perms >>= 8; | 650 | + std::string k = " \xff\xff\xff\xffTadb "; |
| 651 | + int perms = getP(); | ||
| 652 | + for (size_t i = 0; i < 4; ++i) { | ||
| 653 | + k[i] = static_cast<char>(perms & 0xff); | ||
| 654 | + perms >>= 8; | ||
| 657 | } | 655 | } |
| 658 | - k[8] = getEncryptMetadata() ? 'T' : 'F'; | ||
| 659 | - k[9] = 'a'; | ||
| 660 | - k[10] = 'd'; | ||
| 661 | - k[11] = 'b'; | ||
| 662 | - QUtil::initializeWithRandomBytes(k + 12, 4); | 656 | + if (!getEncryptMetadata()) { |
| 657 | + k[8] = 'F'; | ||
| 658 | + } | ||
| 659 | + QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(&k[12]), 4); | ||
| 660 | + return k; | ||
| 663 | } | 661 | } |
| 664 | 662 | ||
| 665 | std::string | 663 | std::string |
| 666 | QPDF::EncryptionData::compute_Perms_value_V5(std::string const& encryption_key) const | 664 | QPDF::EncryptionData::compute_Perms_value_V5(std::string const& encryption_key) const |
| 667 | { | 665 | { |
| 668 | // Algorithm 3.10 from the PDF 1.7 extension level 3 | 666 | // Algorithm 3.10 from the PDF 1.7 extension level 3 |
| 669 | - unsigned char k[16]; | ||
| 670 | - compute_Perms_value_V5_clear(encryption_key, k); | ||
| 671 | - return process_with_aes( | ||
| 672 | - encryption_key, true, std::string(reinterpret_cast<char*>(k), sizeof(k))); | 667 | + return process_with_aes(encryption_key, true, compute_Perms_value_V5_clear()); |
| 673 | } | 668 | } |
| 674 | 669 | ||
| 675 | std::string | 670 | std::string |
| @@ -700,10 +695,7 @@ QPDF::EncryptionData::recover_encryption_key_with_password( | @@ -700,10 +695,7 @@ QPDF::EncryptionData::recover_encryption_key_with_password( | ||
| 700 | 695 | ||
| 701 | // Decrypt Perms and check against expected value | 696 | // Decrypt Perms and check against expected value |
| 702 | auto perms_check = process_with_aes(file_key, false, getPerms()).substr(0, 12); | 697 | auto perms_check = process_with_aes(file_key, false, getPerms()).substr(0, 12); |
| 703 | - unsigned char k[16]; | ||
| 704 | - compute_Perms_value_V5_clear(file_key, k); | ||
| 705 | - perms_valid = (memcmp(perms_check.c_str(), k, 12) == 0); | ||
| 706 | - | 698 | + perms_valid = compute_Perms_value_V5_clear().substr(0, 12) == perms_check; |
| 707 | return file_key; | 699 | return file_key; |
| 708 | } | 700 | } |
| 709 | 701 | ||
| @@ -1214,9 +1206,7 @@ QPDF::EncryptionData::compute_encryption_parameters_V5( | @@ -1214,9 +1206,7 @@ QPDF::EncryptionData::compute_encryption_parameters_V5( | ||
| 1214 | std::string& out_UE, | 1206 | std::string& out_UE, |
| 1215 | std::string& out_Perms) | 1207 | std::string& out_Perms) |
| 1216 | { | 1208 | { |
| 1217 | - unsigned char k[key_bytes]; | ||
| 1218 | - QUtil::initializeWithRandomBytes(k, key_bytes); | ||
| 1219 | - out_encryption_key = std::string(reinterpret_cast<char*>(k), key_bytes); | 1209 | + out_encryption_key = util::random_string(key_bytes); |
| 1220 | compute_U_UE_value_V5(user_password, out_encryption_key, out_U, out_UE); | 1210 | compute_U_UE_value_V5(user_password, out_encryption_key, out_U, out_UE); |
| 1221 | compute_O_OE_value_V5(owner_password, out_encryption_key, out_U, out_O, out_OE); | 1211 | compute_O_OE_value_V5(owner_password, out_encryption_key, out_U, out_O, out_OE); |
| 1222 | out_Perms = compute_Perms_value_V5(out_encryption_key); | 1212 | out_Perms = compute_Perms_value_V5(out_encryption_key); |
libqpdf/QUtil.cc
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | #include <qpdf/qpdf-config.h> | 2 | #include <qpdf/qpdf-config.h> |
| 3 | 3 | ||
| 4 | #include <qpdf/QUtil.hh> | 4 | #include <qpdf/QUtil.hh> |
| 5 | +#include <qpdf/Util.hh> | ||
| 5 | 6 | ||
| 6 | #include <qpdf/CryptoRandomDataProvider.hh> | 7 | #include <qpdf/CryptoRandomDataProvider.hh> |
| 7 | #include <qpdf/Pipeline.hh> | 8 | #include <qpdf/Pipeline.hh> |
| @@ -1131,6 +1132,14 @@ QUtil::initializeWithRandomBytes(unsigned char* data, size_t len) | @@ -1131,6 +1132,14 @@ QUtil::initializeWithRandomBytes(unsigned char* data, size_t len) | ||
| 1131 | getRandomDataProvider()->provideRandomData(data, len); | 1132 | getRandomDataProvider()->provideRandomData(data, len); |
| 1132 | } | 1133 | } |
| 1133 | 1134 | ||
| 1135 | +std::string | ||
| 1136 | +util::random_string(size_t len) | ||
| 1137 | +{ | ||
| 1138 | + std::string result(len, '\0'); | ||
| 1139 | + QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(result.data()), len); | ||
| 1140 | + return result; | ||
| 1141 | +} | ||
| 1142 | + | ||
| 1134 | long | 1143 | long |
| 1135 | QUtil::random() | 1144 | QUtil::random() |
| 1136 | { | 1145 | { |
libqpdf/qpdf/Util.hh