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,7 +170,7 @@ QPDF::trim_user_password(std::string& user_password)
170 } 170 }
171 171
172 auto idx = user_password.find('\x28'); 172 auto idx = user_password.find('\x28');
173 - ; 173 +
174 while (idx != user_password.npos) { 174 while (idx != user_password.npos) {
175 if (padding_string.starts_with(user_password.substr(idx))) { 175 if (padding_string.starts_with(user_password.substr(idx))) {
176 user_password.resize(idx); 176 user_password.resize(idx);
@@ -206,23 +206,16 @@ iterate_md5_digest(MD5& md5, int iterations, int key_len) @@ -206,23 +206,16 @@ iterate_md5_digest(MD5& md5, int iterations, int key_len)
206 } 206 }
207 207
208 static void 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 for (int i = 0; i < iterations; ++i) { 213 for (int i = 0; i < iterations; ++i) {
220 int const xor_value = (reverse ? iterations - 1 - i : i); 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,13 +430,7 @@ QPDF::EncryptionData::compute_O_value(
437 auto upass = pad_or_truncate_password_V4(user_password); 430 auto upass = pad_or_truncate_password_V4(user_password);
438 std::string O_key = compute_O_rc4_key(user_password, owner_password); 431 std::string O_key = compute_O_rc4_key(user_password, owner_password);
439 pad_short_parameter(O_key, QIntC::to_size(getLengthBytes())); 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 return upass; 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,13 +442,7 @@ QPDF::EncryptionData::compute_U_value_R2(std::string const&amp; user_password) const
455 std::string k1 = compute_encryption_key(user_password); 442 std::string k1 = compute_encryption_key(user_password);
456 auto udata = padding_string; 443 auto udata = padding_string;
457 pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); 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 return udata; 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,18 +455,12 @@ QPDF::EncryptionData::compute_U_value_R3(std::string const&amp; user_password) const
474 MD5 md5; 455 MD5 md5;
475 md5.encodeDataIncrementally(padding_string); 456 md5.encodeDataIncrementally(padding_string);
476 md5.encodeDataIncrementally(getId1()); 457 md5.encodeDataIncrementally(getId1());
477 - MD5::Digest digest;  
478 - md5.digest(digest); 458 + auto result = md5.digest();
479 pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); 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 // pad with arbitrary data -- make it consistent for the sake of testing 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 std::string 466 std::string
@@ -538,13 +513,7 @@ QPDF::EncryptionData::check_owner_password_V4( @@ -538,13 +513,7 @@ QPDF::EncryptionData::check_owner_password_V4(
538 auto key = compute_O_rc4_key(user_password, owner_password); 513 auto key = compute_O_rc4_key(user_password, owner_password);
539 pad_short_parameter(key, QIntC::to_size(getLengthBytes())); 514 pad_short_parameter(key, QIntC::to_size(getLengthBytes()));
540 auto new_user_password = O.substr(0, key_bytes); 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 if (check_user_password(new_user_password)) { 517 if (check_user_password(new_user_password)) {
549 user_password = new_user_password; 518 user_password = new_user_password;
550 return true; 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,3 +13,13 @@ RC4::process(unsigned char const* in_data, size_t len, unsigned char* out_data)
13 { 13 {
14 crypto->RC4_process(in_data, len, out_data); 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,6 +14,8 @@ class RC4
14 // It is safe to pass the same pointer to in_data and out_data to encrypt/decrypt in place 14 // It is safe to pass the same pointer to in_data and out_data to encrypt/decrypt in place
15 void process(unsigned char const* in_data, size_t len, unsigned char* out_data); 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 private: 19 private:
18 std::shared_ptr<QPDFCryptoImpl> crypto; 20 std::shared_ptr<QPDFCryptoImpl> crypto;
19 }; 21 };