Commit 04cc9df216a79689f8ba4e7746ceb0f14749f0cb

Authored by m-holger
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.
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&amp; 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&amp; 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 };
... ...