Commit aab00a1a04a759df02b9abd6d4ef2ae4ff8575d7
1 parent
60d0ff6c
Refactor `QPDF_encryption` to replace raw character arrays with `std::string`, s…
…implify padding logic, and streamline password handling across methods.
Showing
1 changed file
with
36 additions
and
50 deletions
libqpdf/QPDF_encryption.cc
| @@ -22,10 +22,11 @@ | @@ -22,10 +22,11 @@ | ||
| 22 | #include <cstring> | 22 | #include <cstring> |
| 23 | 23 | ||
| 24 | using namespace qpdf; | 24 | using namespace qpdf; |
| 25 | +using namespace std::literals; | ||
| 25 | 26 | ||
| 26 | -static unsigned char const padding_string[] = { | ||
| 27 | - 0x28, 0xbf, 0x4e, 0x5e, 0x4e, 0x75, 0x8a, 0x41, 0x64, 0x00, 0x4e, 0x56, 0xff, 0xfa, 0x01, 0x08, | ||
| 28 | - 0x2e, 0x2e, 0x00, 0xb6, 0xd0, 0x68, 0x3e, 0x80, 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a}; | 27 | +static std::string padding_string = |
| 28 | + "\x28\xbf\x4e\x5e\x4e\x75\x8a\x41\x64\x00\x4e\x56\xff\xfa\x01\x08" | ||
| 29 | + "\x2e\x2e\x00\xb6\xd0\x68\x3e\x80\x2f\x0c\xa9\xfe\x64\x53\x69\x7a"s; | ||
| 29 | 30 | ||
| 30 | static unsigned int const key_bytes = 32; | 31 | static unsigned int const key_bytes = 32; |
| 31 | 32 | ||
| @@ -159,46 +160,35 @@ QPDF::EncryptionData::setV5EncryptionParameters( | @@ -159,46 +160,35 @@ QPDF::EncryptionData::setV5EncryptionParameters( | ||
| 159 | this->Perms = Perms; | 160 | this->Perms = Perms; |
| 160 | } | 161 | } |
| 161 | 162 | ||
| 162 | -static void | ||
| 163 | -pad_or_truncate_password_V4(std::string const& password, char k1[key_bytes]) | ||
| 164 | -{ | ||
| 165 | - size_t password_bytes = std::min(QIntC::to_size(key_bytes), password.length()); | ||
| 166 | - size_t pad_bytes = key_bytes - password_bytes; | ||
| 167 | - memcpy(k1, password.c_str(), password_bytes); | ||
| 168 | - memcpy(k1 + password_bytes, padding_string, pad_bytes); | ||
| 169 | -} | ||
| 170 | - | ||
| 171 | void | 163 | void |
| 172 | QPDF::trim_user_password(std::string& user_password) | 164 | QPDF::trim_user_password(std::string& user_password) |
| 173 | { | 165 | { |
| 174 | // Although unnecessary, this routine trims the padding string from the end of a user password. | 166 | // Although unnecessary, this routine trims the padding string from the end of a user password. |
| 175 | // Its only purpose is for recovery of user passwords which is done in the test suite. | 167 | // Its only purpose is for recovery of user passwords which is done in the test suite. |
| 176 | - char const* cstr = user_password.c_str(); | ||
| 177 | - size_t len = user_password.length(); | ||
| 178 | - if (len < key_bytes) { | 168 | + if (user_password.size() < key_bytes) { |
| 179 | return; | 169 | return; |
| 180 | } | 170 | } |
| 181 | 171 | ||
| 182 | - char const* p1 = cstr; | ||
| 183 | - char const* p2 = nullptr; | ||
| 184 | - while ((p2 = strchr(p1, '\x28')) != nullptr) { | ||
| 185 | - size_t idx = toS(p2 - cstr); | ||
| 186 | - if (memcmp(p2, padding_string, len - idx) == 0) { | ||
| 187 | - user_password = user_password.substr(0, idx); | 172 | + auto idx = user_password.find('\x28'); |
| 173 | + ; | ||
| 174 | + while (idx != user_password.npos) { | ||
| 175 | + if (padding_string.starts_with(user_password.substr(idx))) { | ||
| 176 | + user_password.resize(idx); | ||
| 188 | return; | 177 | return; |
| 189 | - } else { | ||
| 190 | - QTC::TC("qpdf", "QPDF_encryption skip 0x28"); | ||
| 191 | - p1 = p2 + 1; | ||
| 192 | } | 178 | } |
| 179 | + QTC::TC("qpdf", "QPDF_encryption skip 0x28"); | ||
| 180 | + idx = user_password.find('\x28', ++idx); | ||
| 193 | } | 181 | } |
| 194 | } | 182 | } |
| 195 | 183 | ||
| 196 | static std::string | 184 | static std::string |
| 197 | -pad_or_truncate_password_V4(std::string const& password) | 185 | +pad_or_truncate_password_V4(std::string password) |
| 198 | { | 186 | { |
| 199 | - char k1[key_bytes]; | ||
| 200 | - pad_or_truncate_password_V4(password, k1); | ||
| 201 | - return {k1, key_bytes}; | 187 | + if (password.size() < key_bytes) { |
| 188 | + password.append(padding_string); | ||
| 189 | + } | ||
| 190 | + password.resize(key_bytes); | ||
| 191 | + return password; | ||
| 202 | } | 192 | } |
| 203 | 193 | ||
| 204 | static std::string | 194 | static std::string |
| @@ -381,7 +371,7 @@ QPDF::compute_data_key( | @@ -381,7 +371,7 @@ QPDF::compute_data_key( | ||
| 381 | } | 371 | } |
| 382 | 372 | ||
| 383 | MD5 md5; | 373 | MD5 md5; |
| 384 | - md5.encodeDataIncrementally(result.c_str(), result.length()); | 374 | + md5.encodeDataIncrementally(result); |
| 385 | MD5::Digest digest; | 375 | MD5::Digest digest; |
| 386 | md5.digest(digest); | 376 | md5.digest(digest); |
| 387 | return {reinterpret_cast<char*>(digest), std::min(result.length(), toS(16))}; | 377 | return {reinterpret_cast<char*>(digest), std::min(result.length(), toS(16))}; |
| @@ -418,20 +408,18 @@ QPDF::EncryptionData::compute_encryption_key_from_password(std::string const& pa | @@ -418,20 +408,18 @@ QPDF::EncryptionData::compute_encryption_key_from_password(std::string const& pa | ||
| 418 | // password to be presented in its final form. | 408 | // password to be presented in its final form. |
| 419 | 409 | ||
| 420 | MD5 md5; | 410 | MD5 md5; |
| 421 | - md5.encodeDataIncrementally(pad_or_truncate_password_V4(password).c_str(), key_bytes); | ||
| 422 | - md5.encodeDataIncrementally(getO().c_str(), key_bytes); | 411 | + md5.encodeDataIncrementally(pad_or_truncate_password_V4(password)); |
| 412 | + md5.encodeDataIncrementally(getO()); | ||
| 423 | char pbytes[4]; | 413 | char pbytes[4]; |
| 424 | - int P = getP(); | ||
| 425 | - pbytes[0] = static_cast<char>(P & 0xff); | ||
| 426 | - pbytes[1] = static_cast<char>((P >> 8) & 0xff); | ||
| 427 | - pbytes[2] = static_cast<char>((P >> 16) & 0xff); | ||
| 428 | - pbytes[3] = static_cast<char>((P >> 24) & 0xff); | 414 | + int p = getP(); |
| 415 | + pbytes[0] = static_cast<char>(p & 0xff); | ||
| 416 | + pbytes[1] = static_cast<char>((p >> 8) & 0xff); | ||
| 417 | + pbytes[2] = static_cast<char>((p >> 16) & 0xff); | ||
| 418 | + pbytes[3] = static_cast<char>((p >> 24) & 0xff); | ||
| 429 | md5.encodeDataIncrementally(pbytes, 4); | 419 | md5.encodeDataIncrementally(pbytes, 4); |
| 430 | - md5.encodeDataIncrementally(getId1().c_str(), getId1().length()); | 420 | + md5.encodeDataIncrementally(getId1()); |
| 431 | if (getR() >= 4 && !getEncryptMetadata()) { | 421 | if (getR() >= 4 && !getEncryptMetadata()) { |
| 432 | - char bytes[4]; | ||
| 433 | - memset(bytes, 0xff, 4); | ||
| 434 | - md5.encodeDataIncrementally(bytes, 4); | 422 | + md5.encodeDataIncrementally("\xff\xff\xff\xff"); |
| 435 | } | 423 | } |
| 436 | MD5::Digest digest; | 424 | MD5::Digest digest; |
| 437 | int key_len = std::min(toI(sizeof(digest)), getLengthBytes()); | 425 | int key_len = std::min(toI(sizeof(digest)), getLengthBytes()); |
| @@ -453,7 +441,7 @@ QPDF::EncryptionData::compute_O_rc4_key( | @@ -453,7 +441,7 @@ QPDF::EncryptionData::compute_O_rc4_key( | ||
| 453 | password = user_password; | 441 | password = user_password; |
| 454 | } | 442 | } |
| 455 | MD5 md5; | 443 | MD5 md5; |
| 456 | - md5.encodeDataIncrementally(pad_or_truncate_password_V4(password).c_str(), key_bytes); | 444 | + md5.encodeDataIncrementally(pad_or_truncate_password_V4(password)); |
| 457 | MD5::Digest digest; | 445 | MD5::Digest digest; |
| 458 | int key_len = std::min(QIntC::to_int(sizeof(digest)), getLengthBytes()); | 446 | int key_len = std::min(QIntC::to_int(sizeof(digest)), getLengthBytes()); |
| 459 | iterate_md5_digest(md5, digest, (getR() >= 3 ? 50 : 0), key_len); | 447 | iterate_md5_digest(md5, digest, (getR() >= 3 ? 50 : 0), key_len); |
| @@ -469,8 +457,7 @@ QPDF::EncryptionData::compute_O_value( | @@ -469,8 +457,7 @@ QPDF::EncryptionData::compute_O_value( | ||
| 469 | unsigned char O_key[OU_key_bytes_V4]; | 457 | unsigned char O_key[OU_key_bytes_V4]; |
| 470 | compute_O_rc4_key(user_password, owner_password, O_key); | 458 | compute_O_rc4_key(user_password, owner_password, O_key); |
| 471 | 459 | ||
| 472 | - char upass[key_bytes]; | ||
| 473 | - pad_or_truncate_password_V4(user_password, upass); | 460 | + auto upass = pad_or_truncate_password_V4(user_password); |
| 474 | std::string k1(reinterpret_cast<char*>(O_key), OU_key_bytes_V4); | 461 | std::string k1(reinterpret_cast<char*>(O_key), OU_key_bytes_V4); |
| 475 | pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); | 462 | pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |
| 476 | iterate_rc4( | 463 | iterate_rc4( |
| @@ -480,7 +467,7 @@ QPDF::EncryptionData::compute_O_value( | @@ -480,7 +467,7 @@ QPDF::EncryptionData::compute_O_value( | ||
| 480 | getLengthBytes(), | 467 | getLengthBytes(), |
| 481 | getR() >= 3 ? 20 : 1, | 468 | getR() >= 3 ? 20 : 1, |
| 482 | false); | 469 | false); |
| 483 | - return {upass, key_bytes}; | 470 | + return upass; |
| 484 | } | 471 | } |
| 485 | 472 | ||
| 486 | std::string | 473 | std::string |
| @@ -489,17 +476,16 @@ QPDF::EncryptionData::compute_U_value_R2(std::string const& user_password) const | @@ -489,17 +476,16 @@ QPDF::EncryptionData::compute_U_value_R2(std::string const& user_password) const | ||
| 489 | // Algorithm 3.4 from the PDF 1.7 Reference Manual | 476 | // Algorithm 3.4 from the PDF 1.7 Reference Manual |
| 490 | 477 | ||
| 491 | std::string k1 = compute_encryption_key(user_password); | 478 | std::string k1 = compute_encryption_key(user_password); |
| 492 | - char udata[key_bytes]; | ||
| 493 | - pad_or_truncate_password_V4("", udata); | 479 | + auto udata = padding_string; |
| 494 | pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); | 480 | pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |
| 495 | iterate_rc4( | 481 | iterate_rc4( |
| 496 | - QUtil::unsigned_char_pointer(udata), | 482 | + QUtil::unsigned_char_pointer(udata.data()), |
| 497 | key_bytes, | 483 | key_bytes, |
| 498 | QUtil::unsigned_char_pointer(k1), | 484 | QUtil::unsigned_char_pointer(k1), |
| 499 | getLengthBytes(), | 485 | getLengthBytes(), |
| 500 | 1, | 486 | 1, |
| 501 | false); | 487 | false); |
| 502 | - return {udata, key_bytes}; | 488 | + return udata; |
| 503 | } | 489 | } |
| 504 | 490 | ||
| 505 | std::string | 491 | std::string |
| @@ -509,8 +495,8 @@ QPDF::EncryptionData::compute_U_value_R3(std::string const& user_password) const | @@ -509,8 +495,8 @@ QPDF::EncryptionData::compute_U_value_R3(std::string const& user_password) const | ||
| 509 | 495 | ||
| 510 | std::string k1 = compute_encryption_key(user_password); | 496 | std::string k1 = compute_encryption_key(user_password); |
| 511 | MD5 md5; | 497 | MD5 md5; |
| 512 | - md5.encodeDataIncrementally(pad_or_truncate_password_V4("").c_str(), key_bytes); | ||
| 513 | - md5.encodeDataIncrementally(getId1().c_str(), getId1().length()); | 498 | + md5.encodeDataIncrementally(padding_string); |
| 499 | + md5.encodeDataIncrementally(getId1()); | ||
| 514 | MD5::Digest digest; | 500 | MD5::Digest digest; |
| 515 | md5.digest(digest); | 501 | md5.digest(digest); |
| 516 | pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); | 502 | pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |