Commit 04cc9df216a79689f8ba4e7746ceb0f14749f0cb
1 parent
e81abdcc
Refactor `QPDF_encryption` and `RC4` to replace raw arrays with `std::string`, c…
…onsolidate `iterate_rc4` logic, and streamline RC4 key processing.
Showing
3 changed files
with
26 additions
and
45 deletions
libqpdf/QPDF_encryption.cc
| ... | ... | @@ -170,7 +170,7 @@ QPDF::trim_user_password(std::string& user_password) |
| 170 | 170 | } |
| 171 | 171 | |
| 172 | 172 | auto idx = user_password.find('\x28'); |
| 173 | - ; | |
| 173 | + | |
| 174 | 174 | while (idx != user_password.npos) { |
| 175 | 175 | if (padding_string.starts_with(user_password.substr(idx))) { |
| 176 | 176 | user_password.resize(idx); |
| ... | ... | @@ -206,23 +206,16 @@ iterate_md5_digest(MD5& md5, int iterations, int key_len) |
| 206 | 206 | } |
| 207 | 207 | |
| 208 | 208 | static void |
| 209 | -iterate_rc4( | |
| 210 | - unsigned char* data, | |
| 211 | - size_t data_len, | |
| 212 | - unsigned char* okey, | |
| 213 | - int key_len, | |
| 214 | - int iterations, | |
| 215 | - bool reverse) | |
| 209 | +iterate_rc4(std::string& data, std::string_view okey, int iterations, bool reverse) | |
| 216 | 210 | { |
| 217 | - auto key_ph = std::make_unique<unsigned char[]>(QIntC::to_size(key_len)); | |
| 218 | - unsigned char* key = key_ph.get(); | |
| 211 | + auto len = okey.size(); | |
| 212 | + std::string key(len, '\0'); | |
| 219 | 213 | for (int i = 0; i < iterations; ++i) { |
| 220 | 214 | int const xor_value = (reverse ? iterations - 1 - i : i); |
| 221 | - for (int j = 0; j < key_len; ++j) { | |
| 222 | - key[j] = static_cast<unsigned char>(okey[j] ^ xor_value); | |
| 215 | + for (size_t j = 0; j < len; ++j) { | |
| 216 | + key[j] = static_cast<char>(okey[j] ^ xor_value); | |
| 223 | 217 | } |
| 224 | - RC4 rc4(key, QIntC::to_int(key_len)); | |
| 225 | - rc4.process(data, data_len, data); | |
| 218 | + RC4::process(key, data); | |
| 226 | 219 | } |
| 227 | 220 | } |
| 228 | 221 | |
| ... | ... | @@ -437,13 +430,7 @@ QPDF::EncryptionData::compute_O_value( |
| 437 | 430 | auto upass = pad_or_truncate_password_V4(user_password); |
| 438 | 431 | std::string O_key = compute_O_rc4_key(user_password, owner_password); |
| 439 | 432 | pad_short_parameter(O_key, QIntC::to_size(getLengthBytes())); |
| 440 | - iterate_rc4( | |
| 441 | - QUtil::unsigned_char_pointer(upass), | |
| 442 | - key_bytes, | |
| 443 | - QUtil::unsigned_char_pointer(O_key.data()), | |
| 444 | - getLengthBytes(), | |
| 445 | - getR() >= 3 ? 20 : 1, | |
| 446 | - false); | |
| 433 | + iterate_rc4(upass, O_key, getR() >= 3 ? 20 : 1, false); | |
| 447 | 434 | return upass; |
| 448 | 435 | } |
| 449 | 436 | |
| ... | ... | @@ -455,13 +442,7 @@ QPDF::EncryptionData::compute_U_value_R2(std::string const& user_password) const |
| 455 | 442 | std::string k1 = compute_encryption_key(user_password); |
| 456 | 443 | auto udata = padding_string; |
| 457 | 444 | pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |
| 458 | - iterate_rc4( | |
| 459 | - QUtil::unsigned_char_pointer(udata.data()), | |
| 460 | - key_bytes, | |
| 461 | - QUtil::unsigned_char_pointer(k1), | |
| 462 | - getLengthBytes(), | |
| 463 | - 1, | |
| 464 | - false); | |
| 445 | + iterate_rc4(udata, k1, 1, false); | |
| 465 | 446 | return udata; |
| 466 | 447 | } |
| 467 | 448 | |
| ... | ... | @@ -474,18 +455,12 @@ QPDF::EncryptionData::compute_U_value_R3(std::string const& user_password) const |
| 474 | 455 | MD5 md5; |
| 475 | 456 | md5.encodeDataIncrementally(padding_string); |
| 476 | 457 | md5.encodeDataIncrementally(getId1()); |
| 477 | - MD5::Digest digest; | |
| 478 | - md5.digest(digest); | |
| 458 | + auto result = md5.digest(); | |
| 479 | 459 | pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |
| 480 | - iterate_rc4( | |
| 481 | - digest, sizeof(MD5::Digest), QUtil::unsigned_char_pointer(k1), getLengthBytes(), 20, false); | |
| 482 | - char result[key_bytes]; | |
| 483 | - memcpy(result, digest, sizeof(MD5::Digest)); | |
| 460 | + iterate_rc4(result, k1, 20, false); | |
| 484 | 461 | // pad with arbitrary data -- make it consistent for the sake of testing |
| 485 | - for (unsigned int i = sizeof(MD5::Digest); i < key_bytes; ++i) { | |
| 486 | - result[i] = static_cast<char>((i * i) % 0xff); | |
| 487 | - } | |
| 488 | - return {result, key_bytes}; | |
| 462 | + result += "\x0\x21\x44\x69\x90\xb9\xe4\x11\x40\x71\xa4\xd9\x10\x49\x84\xc1"s; | |
| 463 | + return result; | |
| 489 | 464 | } |
| 490 | 465 | |
| 491 | 466 | std::string |
| ... | ... | @@ -538,13 +513,7 @@ QPDF::EncryptionData::check_owner_password_V4( |
| 538 | 513 | auto key = compute_O_rc4_key(user_password, owner_password); |
| 539 | 514 | pad_short_parameter(key, QIntC::to_size(getLengthBytes())); |
| 540 | 515 | auto new_user_password = O.substr(0, key_bytes); |
| 541 | - iterate_rc4( | |
| 542 | - QUtil::unsigned_char_pointer(new_user_password.data()), | |
| 543 | - key_bytes, | |
| 544 | - QUtil::unsigned_char_pointer(key), | |
| 545 | - getLengthBytes(), | |
| 546 | - (getR() >= 3) ? 20 : 1, | |
| 547 | - true); | |
| 516 | + iterate_rc4(new_user_password, key, (getR() >= 3) ? 20 : 1, true); | |
| 548 | 517 | if (check_user_password(new_user_password)) { |
| 549 | 518 | user_password = new_user_password; |
| 550 | 519 | return true; | ... | ... |
libqpdf/RC4.cc
| ... | ... | @@ -13,3 +13,13 @@ RC4::process(unsigned char const* in_data, size_t len, unsigned char* out_data) |
| 13 | 13 | { |
| 14 | 14 | crypto->RC4_process(in_data, len, out_data); |
| 15 | 15 | } |
| 16 | + | |
| 17 | +void | |
| 18 | +RC4::process(std::string_view key, std::string& data) | |
| 19 | +{ | |
| 20 | + RC4 rc4(reinterpret_cast<unsigned char const*>(key.data()), static_cast<int>(key.size())); | |
| 21 | + rc4.process( | |
| 22 | + reinterpret_cast<unsigned char const*>(data.data()), | |
| 23 | + data.size(), | |
| 24 | + reinterpret_cast<unsigned char*>(data.data())); | |
| 25 | +} | ... | ... |
libqpdf/qpdf/RC4.hh
| ... | ... | @@ -14,6 +14,8 @@ class RC4 |
| 14 | 14 | // It is safe to pass the same pointer to in_data and out_data to encrypt/decrypt in place |
| 15 | 15 | void process(unsigned char const* in_data, size_t len, unsigned char* out_data); |
| 16 | 16 | |
| 17 | + static void process(std::string_view key, std::string& data); | |
| 18 | + | |
| 17 | 19 | private: |
| 18 | 20 | std::shared_ptr<QPDFCryptoImpl> crypto; |
| 19 | 21 | }; | ... | ... |