Coverage Report

Created: 2025-08-28 06:31

/src/qpdf/libqpdf/QPDF_encryption.cc
Line
Count
Source (jump to first uncovered line)
1
// This file implements methods from the QPDF class that involve
2
// encryption.
3
4
#include <qpdf/assert_debug.h>
5
6
#include <qpdf/QPDF_private.hh>
7
8
#include <qpdf/QPDFExc.hh>
9
10
#include <qpdf/MD5.hh>
11
#include <qpdf/Pl_AES_PDF.hh>
12
#include <qpdf/Pl_Buffer.hh>
13
#include <qpdf/Pl_RC4.hh>
14
#include <qpdf/Pl_SHA2.hh>
15
#include <qpdf/QPDFObjectHandle_private.hh>
16
#include <qpdf/QTC.hh>
17
#include <qpdf/QUtil.hh>
18
#include <qpdf/RC4.hh>
19
#include <qpdf/Util.hh>
20
21
#include <algorithm>
22
#include <cstring>
23
24
using namespace qpdf;
25
using namespace std::literals;
26
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;
30
31
static unsigned int const key_bytes = 32;
32
33
static unsigned int const OU_key_bytes_V5 = 48;
34
static unsigned int const OUE_key_bytes_V5 = 32;
35
static unsigned int const Perms_key_bytes_V5 = 16;
36
37
int
38
QPDF::EncryptionData::getV() const
39
0
{
40
0
    return this->V;
41
0
}
42
43
int
44
QPDF::EncryptionData::getR() const
45
0
{
46
0
    return this->R;
47
0
}
48
49
int
50
QPDF::EncryptionData::getLengthBytes() const
51
0
{
52
0
    return this->Length_bytes;
53
0
}
54
55
int
56
QPDF::EncryptionData::getP() const
57
0
{
58
0
    return static_cast<int>(P.to_ulong());
59
0
}
60
61
bool
62
QPDF::EncryptionData::getP(size_t bit) const
63
0
{
64
0
    qpdf_assert_debug(bit);
65
0
    return P.test(bit - 1);
66
0
}
67
68
bool
69
QPDF::EncryptionParameters::P(size_t bit) const
70
0
{
71
0
    qpdf_assert_debug(bit);
72
0
    return P_.test(bit - 1);
73
0
}
74
75
std::string const&
76
QPDF::EncryptionData::getO() const
77
0
{
78
0
    return this->O;
79
0
}
80
81
std::string const&
82
QPDF::EncryptionData::getU() const
83
0
{
84
0
    return this->U;
85
0
}
86
87
std::string const&
88
QPDF::EncryptionData::getOE() const
89
0
{
90
0
    return this->OE;
91
0
}
92
93
std::string const&
94
QPDF::EncryptionData::getUE() const
95
0
{
96
0
    return this->UE;
97
0
}
98
99
std::string const&
100
QPDF::EncryptionData::getPerms() const
101
0
{
102
0
    return this->Perms;
103
0
}
104
105
std::string const&
106
QPDF::EncryptionData::getId1() const
107
0
{
108
0
    return this->id1;
109
0
}
110
111
bool
112
QPDF::EncryptionData::getEncryptMetadata() const
113
0
{
114
0
    return this->encrypt_metadata;
115
0
}
116
117
void
118
QPDF::EncryptionData::setO(std::string const& O)
119
0
{
120
0
    this->O = O;
121
0
}
122
123
void
124
QPDF::EncryptionData::setU(std::string const& U)
125
0
{
126
0
    this->U = U;
127
0
}
128
129
void
130
QPDF::EncryptionData::setP(size_t bit, bool val)
131
0
{
132
0
    qpdf_assert_debug(bit);
133
0
    P.set(bit - 1, val);
134
0
}
135
136
void
137
QPDF::EncryptionData::setP(unsigned long val)
138
0
{
139
0
    P = std::bitset<32>(val);
140
0
}
141
142
void
143
QPDF::EncryptionData::setId1(std::string const& val)
144
0
{
145
0
    id1 = val;
146
0
}
147
148
void
149
QPDF::EncryptionData::setV5EncryptionParameters(
150
    std::string const& O,
151
    std::string const& OE,
152
    std::string const& U,
153
    std::string const& UE,
154
    std::string const& Perms)
155
0
{
156
0
    this->O = O;
157
0
    this->OE = OE;
158
0
    this->U = U;
159
0
    this->UE = UE;
160
0
    this->Perms = Perms;
161
0
}
162
163
void
164
QPDF::trim_user_password(std::string& user_password)
165
0
{
166
    // Although unnecessary, this routine trims the padding string from the end of a user password.
167
    // Its only purpose is for recovery of user passwords which is done in the test suite.
168
0
    if (user_password.size() < key_bytes) {
169
0
        return;
170
0
    }
171
172
0
    auto idx = user_password.find('\x28');
173
174
0
    while (idx != user_password.npos) {
175
0
        if (padding_string.starts_with(user_password.substr(idx))) {
176
0
            user_password.resize(idx);
177
0
            return;
178
0
        }
179
0
        QTC::TC("qpdf", "QPDF_encryption skip 0x28");
180
0
        idx = user_password.find('\x28', ++idx);
181
0
    }
182
0
}
183
184
static std::string
185
pad_or_truncate_password_V4(std::string password)
186
0
{
187
0
    if (password.size() < key_bytes) {
188
0
        password.append(padding_string);
189
0
    }
190
0
    password.resize(key_bytes);
191
0
    return password;
192
0
}
193
194
static std::string
195
iterate_md5_digest(MD5& md5, int iterations, int key_len)
196
0
{
197
0
    MD5::Digest digest;
198
0
    md5.digest(digest);
199
0
    auto len = std::min(QIntC::to_size(key_len), sizeof(digest));
200
0
    for (int i = 0; i < iterations; ++i) {
201
0
        MD5 m;
202
0
        m.encodeDataIncrementally(reinterpret_cast<char*>(digest), len);
203
0
        m.digest(digest);
204
0
    }
205
0
    return {reinterpret_cast<char*>(digest), len};
206
0
}
207
208
static void
209
iterate_rc4(std::string& data, std::string_view okey, int iterations, bool reverse)
210
0
{
211
0
    auto len = okey.size();
212
0
    std::string key(len, '\0');
213
0
    for (int i = 0; i < iterations; ++i) {
214
0
        int const xor_value = (reverse ? iterations - 1 - i : i);
215
0
        for (size_t j = 0; j < len; ++j) {
216
0
            key[j] = static_cast<char>(okey[j] ^ xor_value);
217
0
        }
218
0
        RC4::process(key, data);
219
0
    }
220
0
}
221
222
static std::string
223
process_with_aes(
224
    std::string const& key,
225
    bool encrypt,
226
    std::string const& data,
227
    size_t outlength = 0,
228
    unsigned int repetitions = 1,
229
    unsigned char const* iv = nullptr,
230
    size_t iv_length = 0)
231
0
{
232
0
    Pl_Buffer buffer("buffer");
233
0
    Pl_AES_PDF aes("aes", &buffer, encrypt, key);
234
0
    if (iv) {
235
0
        aes.setIV(iv, iv_length);
236
0
    } else {
237
0
        aes.useZeroIV();
238
0
    }
239
0
    aes.disablePadding();
240
0
    for (unsigned int i = 0; i < repetitions; ++i) {
241
0
        aes.writeString(data);
242
0
    }
243
0
    aes.finish();
244
0
    if (outlength == 0) {
245
0
        return buffer.getString();
246
0
    } else {
247
0
        return buffer.getString().substr(0, outlength);
248
0
    }
249
0
}
250
251
std::string
252
QPDF::EncryptionData::hash_V5(
253
    std::string const& password, std::string const& salt, std::string const& udata) const
254
0
{
255
0
    Pl_SHA2 hash(256);
256
0
    hash.writeString(password);
257
0
    hash.writeString(salt);
258
0
    hash.writeString(udata);
259
0
    hash.finish();
260
0
    std::string K = hash.getRawDigest();
261
262
0
    std::string result;
263
0
    if (getR() < 6) {
264
0
        result = K;
265
0
    } else {
266
        // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash
267
268
0
        int round_number = 0;
269
0
        bool done = false;
270
0
        while (!done) {
271
            // The hash algorithm has us setting K initially to the R5 value and then repeating a
272
            // series of steps 64 times before starting with the termination case testing.  The
273
            // wording of the specification is very unclear as to the exact number of times it
274
            // should be run since the wording about whether the initial setup counts as round 0 or
275
            // not is ambiguous.  This code counts the initial setup (R5) value as round 0, which
276
            // appears to be correct.  This was determined to be correct by increasing or decreasing
277
            // the number of rounds by 1 or 2 from this value and generating 20 test files.  In this
278
            // interpretation, all the test files worked with Adobe Reader X.  In the other
279
            // configurations, many of the files did not work, and we were accurately able to
280
            // predict which files didn't work by looking at the conditions under which we
281
            // terminated repetition.
282
283
0
            ++round_number;
284
0
            std::string K1 = password + K + udata;
285
0
            qpdf_assert_debug(K.length() >= 32);
286
0
            std::string E = process_with_aes(
287
0
                K.substr(0, 16),
288
0
                true,
289
0
                K1,
290
0
                0,
291
0
                64,
292
0
                QUtil::unsigned_char_pointer(K.substr(16, 16)),
293
0
                16);
294
295
            // E_mod_3 is supposed to be mod 3 of the first 16 bytes of E taken as as a (128-bit)
296
            // big-endian number.  Since (xy mod n) is equal to ((x mod n) + (y mod n)) mod n and
297
            // since 256 mod n is 1, we can just take the sums of the the mod 3s of each byte to get
298
            // the same result.
299
0
            int E_mod_3 = 0;
300
0
            for (unsigned int i = 0; i < 16; ++i) {
301
0
                E_mod_3 += static_cast<unsigned char>(E.at(i));
302
0
            }
303
0
            E_mod_3 %= 3;
304
0
            int next_hash = ((E_mod_3 == 0) ? 256 : (E_mod_3 == 1) ? 384 : 512);
305
0
            Pl_SHA2 sha2(next_hash);
306
0
            sha2.writeString(E);
307
0
            sha2.finish();
308
0
            K = sha2.getRawDigest();
309
310
0
            if (round_number >= 64) {
311
0
                unsigned int ch = static_cast<unsigned char>(*(E.rbegin()));
312
313
0
                if (ch <= QIntC::to_uint(round_number - 32)) {
314
0
                    done = true;
315
0
                }
316
0
            }
317
0
        }
318
0
        result = K.substr(0, 32);
319
0
    }
320
321
0
    return result;
322
0
}
323
324
static void
325
pad_short_parameter(std::string& param, size_t max_len)
326
0
{
327
0
    if (param.length() < max_len) {
328
0
        QTC::TC("qpdf", "QPDF_encryption pad short parameter");
329
0
        param.append(max_len - param.length(), '\0');
330
0
    }
331
0
}
332
333
std::string
334
QPDF::compute_data_key(
335
    std::string const& encryption_key,
336
    int objid,
337
    int generation,
338
    bool use_aes,
339
    int encryption_V,
340
    int encryption_R)
341
0
{
342
    // Algorithm 3.1 from the PDF 1.7 Reference Manual
343
344
0
    std::string result = encryption_key;
345
346
0
    if (encryption_V >= 5) {
347
        // Algorithm 3.1a (PDF 1.7 extension level 3): just use encryption key straight.
348
0
        return result;
349
0
    }
350
351
    // Append low three bytes of object ID and low two bytes of generation
352
0
    result.append(1, static_cast<char>(objid & 0xff));
353
0
    result.append(1, static_cast<char>((objid >> 8) & 0xff));
354
0
    result.append(1, static_cast<char>((objid >> 16) & 0xff));
355
0
    result.append(1, static_cast<char>(generation & 0xff));
356
0
    result.append(1, static_cast<char>((generation >> 8) & 0xff));
357
0
    if (use_aes) {
358
0
        result += "sAlT";
359
0
    }
360
0
    return MD5::digest(result).substr(0, result.size());
361
0
}
362
363
std::string
364
QPDF::compute_encryption_key(std::string const& password, EncryptionData const& data)
365
0
{
366
0
    return data.compute_encryption_key(password);
367
0
}
368
369
std::string
370
QPDF::EncryptionData::compute_encryption_key(std::string const& password) const
371
0
{
372
0
    if (getV() >= 5) {
373
        // For V >= 5, the encryption key is generated and stored in the file, encrypted separately
374
        // with both user and owner passwords.
375
0
        return recover_encryption_key_with_password(password);
376
0
    } else {
377
        // For V < 5, the encryption key is derived from the user
378
        // password.
379
0
        return compute_encryption_key_from_password(password);
380
0
    }
381
0
}
382
383
std::string
384
QPDF::EncryptionData::compute_encryption_key_from_password(std::string const& password) const
385
0
{
386
    // Algorithm 3.2 from the PDF 1.7 Reference Manual
387
388
    // This code does not properly handle Unicode passwords. Passwords are supposed to be converted
389
    // from OS codepage characters to PDFDocEncoding.  Unicode passwords are supposed to be
390
    // converted to OS codepage before converting to PDFDocEncoding.  We instead require the
391
    // password to be presented in its final form.
392
393
0
    MD5 md5;
394
0
    md5.encodeDataIncrementally(pad_or_truncate_password_V4(password));
395
0
    md5.encodeDataIncrementally(getO());
396
0
    char pbytes[4];
397
0
    int p = getP();
398
0
    pbytes[0] = static_cast<char>(p & 0xff);
399
0
    pbytes[1] = static_cast<char>((p >> 8) & 0xff);
400
0
    pbytes[2] = static_cast<char>((p >> 16) & 0xff);
401
0
    pbytes[3] = static_cast<char>((p >> 24) & 0xff);
402
0
    md5.encodeDataIncrementally(pbytes, 4);
403
0
    md5.encodeDataIncrementally(getId1());
404
0
    if (getR() >= 4 && !getEncryptMetadata()) {
405
0
        md5.encodeDataIncrementally("\xff\xff\xff\xff");
406
0
    }
407
0
    return iterate_md5_digest(md5, (getR() >= 3 ? 50 : 0), getLengthBytes());
408
0
}
409
410
std::string
411
QPDF::EncryptionData::compute_O_rc4_key(
412
    std::string const& user_password, std::string const& owner_password) const
413
0
{
414
0
    if (getV() >= 5) {
415
0
        throw std::logic_error("compute_O_rc4_key called for file with V >= 5");
416
0
    }
417
0
    std::string password = owner_password.empty() ? user_password : owner_password;
418
0
    MD5 md5;
419
0
    md5.encodeDataIncrementally(pad_or_truncate_password_V4(password));
420
0
    return iterate_md5_digest(md5, (getR() >= 3 ? 50 : 0), getLengthBytes());
421
0
}
422
423
std::string
424
QPDF::EncryptionData::compute_O_value(
425
    std::string const& user_password, std::string const& owner_password) const
426
0
{
427
    // Algorithm 3.3 from the PDF 1.7 Reference Manual
428
429
0
    auto upass = pad_or_truncate_password_V4(user_password);
430
0
    std::string O_key = compute_O_rc4_key(user_password, owner_password);
431
0
    pad_short_parameter(O_key, QIntC::to_size(getLengthBytes()));
432
0
    iterate_rc4(upass, O_key, getR() >= 3 ? 20 : 1, false);
433
0
    return upass;
434
0
}
435
436
std::string
437
QPDF::EncryptionData::compute_U_value_R2(std::string const& user_password) const
438
0
{
439
    // Algorithm 3.4 from the PDF 1.7 Reference Manual
440
441
0
    std::string k1 = compute_encryption_key(user_password);
442
0
    auto udata = padding_string;
443
0
    pad_short_parameter(k1, QIntC::to_size(getLengthBytes()));
444
0
    iterate_rc4(udata, k1, 1, false);
445
0
    return udata;
446
0
}
447
448
std::string
449
QPDF::EncryptionData::compute_U_value_R3(std::string const& user_password) const
450
0
{
451
    // Algorithm 3.5 from the PDF 1.7 Reference Manual
452
453
0
    std::string k1 = compute_encryption_key(user_password);
454
0
    MD5 md5;
455
0
    md5.encodeDataIncrementally(padding_string);
456
0
    md5.encodeDataIncrementally(getId1());
457
0
    auto result = md5.digest();
458
0
    pad_short_parameter(k1, QIntC::to_size(getLengthBytes()));
459
0
    iterate_rc4(result, k1, 20, false);
460
    // pad with arbitrary data -- make it consistent for the sake of testing
461
0
    result += "\x0\x21\x44\x69\x90\xb9\xe4\x11\x40\x71\xa4\xd9\x10\x49\x84\xc1"s;
462
0
    return result;
463
0
}
464
465
std::string
466
QPDF::EncryptionData::compute_U_value(std::string const& user_password) const
467
0
{
468
0
    if (getR() >= 3) {
469
0
        return compute_U_value_R3(user_password);
470
0
    }
471
472
0
    return compute_U_value_R2(user_password);
473
0
}
474
475
bool
476
QPDF::EncryptionData::check_user_password_V4(std::string const& user_password) const
477
0
{
478
    // Algorithm 3.6 from the PDF 1.7 Reference Manual
479
480
0
    std::string u_value = compute_U_value(user_password);
481
0
    size_t to_compare = (getR() >= 3 ? sizeof(MD5::Digest) : key_bytes);
482
0
    return memcmp(getU().c_str(), u_value.c_str(), to_compare) == 0;
483
0
}
484
485
bool
486
QPDF::EncryptionData::check_user_password_V5(std::string const& user_password) const
487
0
{
488
    // Algorithm 3.11 from the PDF 1.7 extension level 3
489
490
0
    std::string user_data = getU().substr(0, 32);
491
0
    std::string validation_salt = getU().substr(32, 8);
492
0
    std::string password = user_password.substr(0, 127);
493
0
    return hash_V5(user_password.substr(0, 127), validation_salt, "") == user_data;
494
0
}
495
496
bool
497
QPDF::EncryptionData::check_user_password(std::string const& user_password) const
498
0
{
499
0
    if (getV() < 5) {
500
0
        return check_user_password_V4(user_password);
501
0
    } else {
502
0
        return check_user_password_V5(user_password);
503
0
    }
504
0
}
505
506
bool
507
QPDF::EncryptionData::check_owner_password_V4(
508
    std::string& user_password, std::string const& owner_password) const
509
0
{
510
    // Algorithm 3.7 from the PDF 1.7 Reference Manual
511
512
0
    auto key = compute_O_rc4_key(user_password, owner_password);
513
0
    pad_short_parameter(key, QIntC::to_size(getLengthBytes()));
514
0
    auto new_user_password = O.substr(0, key_bytes);
515
0
    iterate_rc4(new_user_password, key, (getR() >= 3) ? 20 : 1, true);
516
0
    if (check_user_password(new_user_password)) {
517
0
        user_password = new_user_password;
518
0
        return true;
519
0
    }
520
0
    return false;
521
0
}
522
523
bool
524
QPDF::EncryptionData::check_owner_password_V5(std::string const& owner_password) const
525
0
{
526
    // Algorithm 3.12 from the PDF 1.7 extension level 3
527
528
0
    std::string user_data = getU().substr(0, 48);
529
0
    std::string owner_data = getO().substr(0, 32);
530
0
    std::string validation_salt = getO().substr(32, 8);
531
0
    return hash_V5(owner_password.substr(0, 127), validation_salt, user_data) == owner_data;
532
0
}
533
534
bool
535
QPDF::EncryptionData::check_owner_password(
536
    std::string& user_password, std::string const& owner_password) const
537
0
{
538
0
    if (getV() < 5) {
539
0
        return check_owner_password_V4(user_password, owner_password);
540
0
    } else {
541
0
        return check_owner_password_V5(owner_password);
542
0
    }
543
0
}
544
545
std::string
546
QPDF::EncryptionData::recover_encryption_key_with_password(std::string const& password) const
547
0
{
548
    // Disregard whether Perms is valid.
549
0
    bool disregard;
550
0
    return recover_encryption_key_with_password(password, disregard);
551
0
}
552
553
std::string
554
QPDF::EncryptionData::compute_Perms_value_V5_clear() const
555
0
{
556
    // From algorithm 3.10 from the PDF 1.7 extension level 3
557
0
    std::string k = "    \xff\xff\xff\xffTadb    ";
558
0
    int perms = getP();
559
0
    for (size_t i = 0; i < 4; ++i) {
560
0
        k[i] = static_cast<char>(perms & 0xff);
561
0
        perms >>= 8;
562
0
    }
563
0
    if (!getEncryptMetadata()) {
564
0
        k[8] = 'F';
565
0
    }
566
0
    QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(&k[12]), 4);
567
0
    return k;
568
0
}
569
570
std::string
571
QPDF::EncryptionData::recover_encryption_key_with_password(
572
    std::string const& password, bool& perms_valid) const
573
0
{
574
    // Algorithm 3.2a from the PDF 1.7 extension level 3
575
576
    // This code does not handle Unicode passwords correctly. Empirical evidence suggests that most
577
    // viewers don't.  We are supposed to process the input string with the SASLprep (RFC 4013)
578
    // profile of stringprep (RFC 3454) and then convert the result to UTF-8.
579
580
0
    perms_valid = false;
581
0
    std::string key_password = password.substr(0, 127);
582
0
    std::string key_salt;
583
0
    std::string user_data;
584
0
    std::string encrypted_file_key;
585
0
    if (check_owner_password_V5(key_password)) {
586
0
        key_salt = getO().substr(40, 8);
587
0
        user_data = getU().substr(0, 48);
588
0
        encrypted_file_key = getOE().substr(0, 32);
589
0
    } else if (check_user_password_V5(key_password)) {
590
0
        key_salt = getU().substr(40, 8);
591
0
        encrypted_file_key = getUE().substr(0, 32);
592
0
    }
593
0
    std::string intermediate_key = hash_V5(key_password, key_salt, user_data);
594
0
    std::string file_key = process_with_aes(intermediate_key, false, encrypted_file_key);
595
596
    // Decrypt Perms and check against expected value
597
0
    auto perms_check = process_with_aes(file_key, false, getPerms()).substr(0, 12);
598
0
    perms_valid = compute_Perms_value_V5_clear().substr(0, 12) == perms_check;
599
0
    return file_key;
600
0
}
601
602
QPDF::encryption_method_e
603
QPDF::EncryptionParameters::interpretCF(QPDFObjectHandle const& cf) const
604
0
{
605
0
    if (!cf.isName()) {
606
        // Default: /Identity
607
0
        return e_none;
608
0
    }
609
0
    std::string filter = cf.getName();
610
0
    auto it = crypt_filters.find(filter);
611
0
    if (it != crypt_filters.end()) {
612
0
        return it->second;
613
0
    }
614
0
    if (filter == "/Identity") {
615
0
        return e_none;
616
0
    }
617
0
    return e_unknown;
618
0
}
619
620
void
621
QPDF::initializeEncryption()
622
7.53k
{
623
7.53k
    m->encp->initialize(*this);
624
7.53k
}
625
626
void
627
QPDF::EncryptionParameters::initialize(QPDF& qpdf)
628
7.53k
{
629
7.53k
    if (encryption_initialized) {
630
0
        return;
631
0
    }
632
7.53k
    encryption_initialized = true;
633
634
7.53k
    auto& qm = *qpdf.m;
635
7.53k
    auto& trailer = qm.trailer;
636
7.53k
    auto& file = qm.file;
637
638
7.53k
    auto warn_damaged_pdf = [&qpdf](std::string const& msg) {
639
0
        qpdf.warn(qpdf.damagedPDF("encryption dictionary", msg));
640
0
    };
641
7.53k
    auto throw_damaged_pdf = [&qpdf](std::string const& msg) {
642
0
        throw qpdf.damagedPDF("encryption dictionary", msg);
643
0
    };
644
7.53k
    auto unsupported = [&file](std::string const& msg) -> QPDFExc {
645
0
        return {
646
0
            qpdf_e_unsupported,
647
0
            file->getName(),
648
0
            "encryption dictionary",
649
0
            file->getLastOffset(),
650
0
            msg};
651
0
    };
652
653
    // After we initialize encryption parameters, we must use stored key information and never look
654
    // at /Encrypt again.  Otherwise, things could go wrong if someone mutates the encryption
655
    // dictionary.
656
657
7.53k
    if (!trailer.hasKey("/Encrypt")) {
658
7.53k
        return;
659
7.53k
    }
660
661
    // Go ahead and set m->encrypted here.  That way, isEncrypted will return true even if there
662
    // were errors reading the encryption dictionary.
663
0
    encrypted = true;
664
665
0
    std::string id1;
666
0
    auto id_obj = trailer.getKey("/ID");
667
0
    if (id_obj.size() != 2 || !id_obj.getArrayItem(0).isString()) {
668
        // Treating a missing ID as the empty string enables qpdf to decrypt some invalid encrypted
669
        // files with no /ID that poppler can read but Adobe Reader can't.
670
0
        qpdf.warn(qpdf.damagedPDF("trailer", "invalid /ID in trailer dictionary"));
671
0
    } else {
672
0
        id1 = id_obj.getArrayItem(0).getStringValue();
673
0
    }
674
675
0
    auto encryption_dict = trailer.getKey("/Encrypt");
676
0
    if (!encryption_dict.isDictionary()) {
677
0
        throw qpdf.damagedPDF("/Encrypt in trailer dictionary is not a dictionary");
678
0
    }
679
680
0
    if (!(encryption_dict.getKey("/Filter").isName() &&
681
0
          (encryption_dict.getKey("/Filter").getName() == "/Standard"))) {
682
0
        throw unsupported("unsupported encryption filter");
683
0
    }
684
0
    if (!encryption_dict.getKey("/SubFilter").isNull()) {
685
0
        qpdf.warn(unsupported("file uses encryption SubFilters, which qpdf does not support"));
686
0
    }
687
688
0
    if (!(encryption_dict.getKey("/V").isInteger() && encryption_dict.getKey("/R").isInteger() &&
689
0
          encryption_dict.getKey("/O").isString() && encryption_dict.getKey("/U").isString() &&
690
0
          encryption_dict.getKey("/P").isInteger())) {
691
0
        throw_damaged_pdf("some encryption dictionary parameters are missing or the wrong type");
692
0
    }
693
694
0
    int V = encryption_dict.getKey("/V").getIntValueAsInt();
695
0
    int R = encryption_dict.getKey("/R").getIntValueAsInt();
696
0
    std::string O = encryption_dict.getKey("/O").getStringValue();
697
0
    std::string U = encryption_dict.getKey("/U").getStringValue();
698
0
    int p = static_cast<int>(encryption_dict.getKey("/P").getIntValue());
699
700
    // If supporting new encryption R/V values, remember to update error message inside this if
701
    // statement.
702
0
    if (!(2 <= R && R <= 6 && (V == 1 || V == 2 || V == 4 || V == 5))) {
703
0
        throw unsupported(
704
0
            "Unsupported /R or /V in encryption dictionary; R = " + std::to_string(R) +
705
0
            " (max 6), V = " + std::to_string(V) + " (max 5)");
706
0
    }
707
708
0
    P_ = std::bitset<32>(static_cast<unsigned long long>(p));
709
0
    encryption_V = V;
710
0
    R_ = R;
711
712
    // OE, UE, and Perms are only present if V >= 5.
713
0
    std::string OE;
714
0
    std::string UE;
715
0
    std::string Perms;
716
717
0
    if (V < 5) {
718
        // These must be exactly the right number of bytes.
719
0
        pad_short_parameter(O, key_bytes);
720
0
        pad_short_parameter(U, key_bytes);
721
0
        if (!(O.length() == key_bytes && U.length() == key_bytes)) {
722
0
            throw_damaged_pdf("incorrect length for /O and/or /U in encryption dictionary");
723
0
        }
724
0
    } else {
725
0
        if (!(encryption_dict.getKey("/OE").isString() &&
726
0
              encryption_dict.getKey("/UE").isString() &&
727
0
              encryption_dict.getKey("/Perms").isString())) {
728
0
            throw_damaged_pdf(
729
0
                "some V=5 encryption dictionary parameters are missing or the wrong type");
730
0
        }
731
0
        OE = encryption_dict.getKey("/OE").getStringValue();
732
0
        UE = encryption_dict.getKey("/UE").getStringValue();
733
0
        Perms = encryption_dict.getKey("/Perms").getStringValue();
734
735
        // These may be longer than the minimum number of bytes.
736
0
        pad_short_parameter(O, OU_key_bytes_V5);
737
0
        pad_short_parameter(U, OU_key_bytes_V5);
738
0
        pad_short_parameter(OE, OUE_key_bytes_V5);
739
0
        pad_short_parameter(UE, OUE_key_bytes_V5);
740
0
        pad_short_parameter(Perms, Perms_key_bytes_V5);
741
0
    }
742
743
0
    int Length = 128; // Just take a guess.
744
0
    if (V <= 1) {
745
0
        Length = 40;
746
0
    } else if (V == 4) {
747
0
        Length = 128;
748
0
    } else if (V == 5) {
749
0
        Length = 256;
750
0
    } else {
751
0
        if (encryption_dict.getKey("/Length").isInteger()) {
752
0
            Length = encryption_dict.getKey("/Length").getIntValueAsInt();
753
0
            if (Length % 8 || Length < 40 || Length > 128) {
754
0
                Length = 128; // Just take a guess.
755
0
            }
756
0
        }
757
0
    }
758
759
0
    encrypt_metadata = true;
760
0
    if (V >= 4 && encryption_dict.getKey("/EncryptMetadata").isBool()) {
761
0
        encrypt_metadata = encryption_dict.getKey("/EncryptMetadata").getBoolValue();
762
0
    }
763
764
0
    if (V == 4 || V == 5) {
765
0
        auto CF = encryption_dict.getKey("/CF");
766
0
        for (auto const& [filter, cdict]: CF.as_dictionary()) {
767
0
            if (cdict.isDictionary()) {
768
0
                encryption_method_e method = e_none;
769
0
                if (cdict.getKey("/CFM").isName()) {
770
0
                    std::string method_name = cdict.getKey("/CFM").getName();
771
0
                    if (method_name == "/V2") {
772
0
                        QTC::TC("qpdf", "QPDF_encryption CFM V2");
773
0
                        method = e_rc4;
774
0
                    } else if (method_name == "/AESV2") {
775
0
                        QTC::TC("qpdf", "QPDF_encryption CFM AESV2");
776
0
                        method = e_aes;
777
0
                    } else if (method_name == "/AESV3") {
778
0
                        QTC::TC("qpdf", "QPDF_encryption CFM AESV3");
779
0
                        method = e_aesv3;
780
0
                    } else {
781
                        // Don't complain now -- maybe we won't need to reference this type.
782
0
                        method = e_unknown;
783
0
                    }
784
0
                }
785
0
                crypt_filters[filter] = method;
786
0
            }
787
0
        }
788
789
0
        cf_stream = interpretCF(encryption_dict.getKey("/StmF"));
790
0
        cf_string = interpretCF(encryption_dict.getKey("/StrF"));
791
0
        if (auto EFF = encryption_dict.getKey("/EFF"); EFF.isName()) {
792
            // qpdf does not use this for anything other than informational purposes. This is
793
            // intended to instruct conforming writers on which crypt filter should be used when new
794
            // file attachments are added to a PDF file, but qpdf never generates encrypted files
795
            // with non-default crypt filters. Prior to 10.2, I was under the mistaken impression
796
            // that this was supposed to be used for decrypting attachments, but the code was wrong
797
            // in a way that turns out not to have mattered because no writers were generating files
798
            // the way I was imagining. Still, providing this information could be useful when
799
            // looking at a file generated by something else, such as Acrobat when specifying that
800
            // only attachments should be encrypted.
801
0
            cf_file = interpretCF(EFF);
802
0
        } else {
803
0
            cf_file = cf_stream;
804
0
        }
805
0
    }
806
807
0
    EncryptionData data(V, R, Length / 8, p, O, U, OE, UE, Perms, id1, encrypt_metadata);
808
0
    if (qm.provided_password_is_hex_key) {
809
        // ignore passwords in file
810
0
        encryption_key = QUtil::hex_decode(provided_password);
811
0
        return;
812
0
    }
813
814
0
    owner_password_matched = data.check_owner_password(user_password, provided_password);
815
0
    if (owner_password_matched && V < 5) {
816
        // password supplied was owner password; user_password has been initialized for V < 5
817
0
        if (qpdf.getTrimmedUserPassword() == provided_password) {
818
0
            user_password_matched = true;
819
0
            QTC::TC("qpdf", "QPDF_encryption user matches owner V < 5");
820
0
        }
821
0
    } else {
822
0
        user_password_matched = data.check_user_password(provided_password);
823
0
        if (user_password_matched) {
824
0
            user_password = provided_password;
825
0
        }
826
0
    }
827
0
    if (user_password_matched && owner_password_matched) {
828
0
        QTC::TC("qpdf", "QPDF_encryption same password", (V < 5) ? 0 : 1);
829
0
    }
830
0
    if (!(owner_password_matched || user_password_matched)) {
831
0
        throw QPDFExc(qpdf_e_password, file->getName(), "", 0, "invalid password");
832
0
    }
833
834
0
    if (V < 5) {
835
        // For V < 5, the user password is encrypted with the owner password, and the user password
836
        // is always used for computing the encryption key.
837
0
        encryption_key = data.compute_encryption_key(user_password);
838
0
    } else {
839
        // For V >= 5, either password can be used independently to compute the encryption key, and
840
        // neither password can be used to recover the other.
841
0
        bool perms_valid;
842
0
        encryption_key = data.recover_encryption_key_with_password(provided_password, perms_valid);
843
0
        if (!perms_valid) {
844
0
            warn_damaged_pdf("/Perms field in encryption dictionary doesn't match expected value");
845
0
        }
846
0
    }
847
0
}
848
849
std::string
850
QPDF::getKeyForObject(std::shared_ptr<EncryptionParameters> encp, QPDFObjGen og, bool use_aes)
851
0
{
852
0
    if (!encp->encrypted) {
853
0
        throw std::logic_error("request for encryption key in non-encrypted PDF");
854
0
    }
855
856
0
    if (og != encp->cached_key_og) {
857
0
        encp->cached_object_encryption_key = compute_data_key(
858
0
            encp->encryption_key, og.getObj(), og.getGen(), use_aes, encp->encryption_V, encp->R());
859
0
        encp->cached_key_og = og;
860
0
    }
861
862
0
    return encp->cached_object_encryption_key;
863
0
}
864
865
void
866
QPDF::decryptString(std::string& str, QPDFObjGen og)
867
0
{
868
0
    if (!og.isIndirect()) {
869
0
        return;
870
0
    }
871
0
    bool use_aes = false;
872
0
    if (m->encp->encryption_V >= 4) {
873
0
        switch (m->encp->cf_string) {
874
0
        case e_none:
875
0
            return;
876
877
0
        case e_aes:
878
0
            use_aes = true;
879
0
            break;
880
881
0
        case e_aesv3:
882
0
            use_aes = true;
883
0
            break;
884
885
0
        case e_rc4:
886
0
            break;
887
888
0
        default:
889
0
            warn(damagedPDF(
890
0
                "unknown encryption filter for strings (check /StrF in "
891
0
                "/Encrypt dictionary); strings may be decrypted improperly"));
892
            // To avoid repeated warnings, reset cf_string.  Assume we'd want to use AES if V == 4.
893
0
            m->encp->cf_string = e_aes;
894
0
            use_aes = true;
895
0
            break;
896
0
        }
897
0
    }
898
899
0
    std::string key = getKeyForObject(m->encp, og, use_aes);
900
0
    try {
901
0
        if (use_aes) {
902
0
            QTC::TC("qpdf", "QPDF_encryption aes decode string");
903
0
            Pl_Buffer bufpl("decrypted string");
904
0
            Pl_AES_PDF pl("aes decrypt string", &bufpl, false, key);
905
0
            pl.writeString(str);
906
0
            pl.finish();
907
0
            str = bufpl.getString();
908
0
        } else {
909
0
            QTC::TC("qpdf", "QPDF_encryption rc4 decode string");
910
0
            size_t vlen = str.length();
911
            // Using std::shared_ptr guarantees that tmp will be freed even if rc4.process throws an
912
            // exception.
913
0
            auto tmp = QUtil::make_unique_cstr(str);
914
0
            RC4 rc4(QUtil::unsigned_char_pointer(key), toI(key.length()));
915
0
            auto data = QUtil::unsigned_char_pointer(tmp.get());
916
0
            rc4.process(data, vlen, data);
917
0
            str = std::string(tmp.get(), vlen);
918
0
        }
919
0
    } catch (QPDFExc&) {
920
0
        throw;
921
0
    } catch (std::runtime_error& e) {
922
0
        throw damagedPDF("error decrypting string for object " + og.unparse() + ": " + e.what());
923
0
    }
924
0
}
925
926
// Prepend a decryption pipeline to 'pipeline'. The decryption pipeline (returned as
927
// 'decrypt_pipeline' must be owned by the caller to ensure that it stays alive while the pipeline
928
// is in use.
929
void
930
QPDF::decryptStream(
931
    std::shared_ptr<EncryptionParameters> encp,
932
    std::shared_ptr<InputSource> file,
933
    QPDF& qpdf_for_warning,
934
    Pipeline*& pipeline,
935
    QPDFObjGen og,
936
    QPDFObjectHandle& stream_dict,
937
    bool is_root_metadata,
938
    std::unique_ptr<Pipeline>& decrypt_pipeline)
939
0
{
940
0
    std::string type;
941
0
    if (stream_dict.getKey("/Type").isName()) {
942
0
        type = stream_dict.getKey("/Type").getName();
943
0
    }
944
0
    if (type == "/XRef") {
945
0
        QTC::TC("qpdf", "QPDF_encryption xref stream from encrypted file");
946
0
        return;
947
0
    }
948
0
    bool use_aes = false;
949
0
    if (encp->encryption_V >= 4) {
950
0
        encryption_method_e method = e_unknown;
951
0
        std::string method_source = "/StmF from /Encrypt dictionary";
952
953
0
        if (stream_dict.getKey("/Filter").isOrHasName("/Crypt")) {
954
0
            if (stream_dict.getKey("/DecodeParms").isDictionary()) {
955
0
                QPDFObjectHandle decode_parms = stream_dict.getKey("/DecodeParms");
956
0
                if (decode_parms.isDictionaryOfType("/CryptFilterDecodeParms")) {
957
0
                    QTC::TC("qpdf", "QPDF_encryption stream crypt filter");
958
0
                    method = encp->interpretCF(decode_parms.getKey("/Name"));
959
0
                    method_source = "stream's Crypt decode parameters";
960
0
                }
961
0
            } else if (
962
0
                stream_dict.getKey("/DecodeParms").isArray() &&
963
0
                stream_dict.getKey("/Filter").isArray()) {
964
0
                auto filter = stream_dict.getKey("/Filter");
965
0
                auto decode = stream_dict.getKey("/DecodeParms");
966
0
                if (filter.size() == decode.size()) {
967
0
                    int i = 0;
968
0
                    for (auto const& item: filter.as_array()) {
969
0
                        if (item.isNameAndEquals("/Crypt")) {
970
0
                            auto crypt_params = decode.getArrayItem(i);
971
0
                            if (crypt_params.isDictionary() &&
972
0
                                crypt_params.getKey("/Name").isName()) {
973
0
                                method = encp->interpretCF(crypt_params.getKey("/Name"));
974
0
                                method_source = "stream's Crypt decode parameters (array)";
975
0
                            }
976
0
                            break;
977
0
                        }
978
0
                        ++i;
979
0
                    }
980
0
                }
981
0
            }
982
0
        }
983
984
0
        if (method == e_unknown) {
985
0
            if ((!encp->encrypt_metadata) && is_root_metadata) {
986
0
                QTC::TC("qpdf", "QPDF_encryption cleartext metadata");
987
0
                method = e_none;
988
0
            } else {
989
0
                method = encp->cf_stream;
990
0
            }
991
0
        }
992
0
        use_aes = false;
993
0
        switch (method) {
994
0
        case e_none:
995
0
            return;
996
0
            break;
997
998
0
        case e_aes:
999
0
            use_aes = true;
1000
0
            break;
1001
1002
0
        case e_aesv3:
1003
0
            use_aes = true;
1004
0
            break;
1005
1006
0
        case e_rc4:
1007
0
            break;
1008
1009
0
        default:
1010
            // filter local to this stream.
1011
0
            qpdf_for_warning.warn(QPDFExc(
1012
0
                qpdf_e_damaged_pdf,
1013
0
                file->getName(),
1014
0
                "",
1015
0
                file->getLastOffset(),
1016
0
                "unknown encryption filter for streams (check " + method_source +
1017
0
                    "); streams may be decrypted improperly"));
1018
            // To avoid repeated warnings, reset cf_stream.  Assume we'd want to use AES if V == 4.
1019
0
            encp->cf_stream = e_aes;
1020
0
            use_aes = true;
1021
0
            break;
1022
0
        }
1023
0
    }
1024
0
    std::string key = getKeyForObject(encp, og, use_aes);
1025
0
    if (use_aes) {
1026
0
        QTC::TC("qpdf", "QPDF_encryption aes decode stream");
1027
0
        decrypt_pipeline =
1028
0
            std::make_unique<Pl_AES_PDF>("AES stream decryption", pipeline, false, key);
1029
0
    } else {
1030
0
        QTC::TC("qpdf", "QPDF_encryption rc4 decode stream");
1031
0
        decrypt_pipeline = std::make_unique<Pl_RC4>("RC4 stream decryption", pipeline, key);
1032
0
    }
1033
0
    pipeline = decrypt_pipeline.get();
1034
0
}
1035
1036
void
1037
QPDF::compute_encryption_O_U(
1038
    char const* user_password,
1039
    char const* owner_password,
1040
    int V,
1041
    int R,
1042
    int key_len,
1043
    int P,
1044
    bool encrypt_metadata,
1045
    std::string const& id1,
1046
    std::string& out_O,
1047
    std::string& out_U)
1048
0
{
1049
0
    EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata);
1050
0
    data.compute_encryption_O_U(user_password, owner_password);
1051
0
    out_O = data.getO();
1052
0
    out_U = data.getU();
1053
0
}
1054
1055
void
1056
QPDF::EncryptionData::compute_encryption_O_U(char const* user_password, char const* owner_password)
1057
0
{
1058
0
    if (V >= 5) {
1059
0
        throw std::logic_error("compute_encryption_O_U called for file with V >= 5");
1060
0
    }
1061
0
    O = compute_O_value(user_password, owner_password);
1062
0
    U = compute_U_value(user_password);
1063
0
}
1064
1065
void
1066
QPDF::compute_encryption_parameters_V5(
1067
    char const* user_password,
1068
    char const* owner_password,
1069
    int V,
1070
    int R,
1071
    int key_len,
1072
    int P,
1073
    bool encrypt_metadata,
1074
    std::string const& id1,
1075
    std::string& encryption_key,
1076
    std::string& out_O,
1077
    std::string& out_U,
1078
    std::string& out_OE,
1079
    std::string& out_UE,
1080
    std::string& out_Perms)
1081
0
{
1082
0
    EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata);
1083
0
    encryption_key = data.compute_encryption_parameters_V5(user_password, owner_password);
1084
1085
0
    out_O = data.getO();
1086
0
    out_U = data.getU();
1087
0
    out_OE = data.getOE();
1088
0
    out_UE = data.getUE();
1089
0
    out_Perms = data.getPerms();
1090
0
}
1091
1092
std::string
1093
QPDF::EncryptionData::compute_encryption_parameters_V5(
1094
    char const* user_password, char const* owner_password)
1095
0
{
1096
0
    auto out_encryption_key = util::random_string(key_bytes);
1097
    // Algorithm 8 from the PDF 2.0
1098
0
    auto validation_salt = util::random_string(8);
1099
0
    auto key_salt = util::random_string(8);
1100
0
    U = hash_V5(user_password, validation_salt, "").append(validation_salt).append(key_salt);
1101
0
    auto intermediate_key = hash_V5(user_password, key_salt, "");
1102
0
    UE = process_with_aes(intermediate_key, true, out_encryption_key);
1103
    // Algorithm 9 from the PDF 2.0
1104
0
    validation_salt = util::random_string(8);
1105
0
    key_salt = util::random_string(8);
1106
0
    O = hash_V5(owner_password, validation_salt, U) + validation_salt + key_salt;
1107
0
    intermediate_key = hash_V5(owner_password, key_salt, U);
1108
0
    OE = process_with_aes(intermediate_key, true, out_encryption_key);
1109
    // Algorithm 10 from the PDF 2.0
1110
0
    Perms = process_with_aes(out_encryption_key, true, compute_Perms_value_V5_clear());
1111
0
    return out_encryption_key;
1112
0
}
1113
1114
std::string
1115
QPDF::EncryptionData::compute_parameters(char const* user_password, char const* owner_password)
1116
0
{
1117
0
    if (V < 5) {
1118
0
        compute_encryption_O_U(user_password, owner_password);
1119
0
        return compute_encryption_key(user_password);
1120
0
    } else {
1121
0
        return compute_encryption_parameters_V5(user_password, owner_password);
1122
0
    }
1123
0
}
1124
1125
std::string const&
1126
QPDF::getPaddedUserPassword() const
1127
0
{
1128
0
    return m->encp->user_password;
1129
0
}
1130
1131
std::string
1132
QPDF::getTrimmedUserPassword() const
1133
0
{
1134
0
    std::string result = m->encp->user_password;
1135
0
    trim_user_password(result);
1136
0
    return result;
1137
0
}
1138
1139
std::string
1140
QPDF::getEncryptionKey() const
1141
0
{
1142
0
    return m->encp->encryption_key;
1143
0
}
1144
1145
bool
1146
QPDF::isEncrypted() const
1147
0
{
1148
0
    return m->encp->encrypted;
1149
0
}
1150
1151
bool
1152
QPDF::isEncrypted(int& R, int& P)
1153
0
{
1154
0
    if (!m->encp->encrypted) {
1155
0
        return false;
1156
0
    }
1157
0
    P = m->encp->P();
1158
0
    R = m->encp->R();
1159
0
    return true;
1160
0
}
1161
1162
bool
1163
QPDF::isEncrypted(
1164
    int& R,
1165
    int& P,
1166
    int& V,
1167
    encryption_method_e& stream_method,
1168
    encryption_method_e& string_method,
1169
    encryption_method_e& file_method)
1170
0
{
1171
0
    if (!m->encp->encrypted) {
1172
0
        return false;
1173
0
    }
1174
0
    P = m->encp->P();
1175
0
    R = m->encp->R();
1176
0
    V = m->encp->encryption_V;
1177
0
    stream_method = m->encp->cf_stream;
1178
0
    string_method = m->encp->cf_string;
1179
0
    file_method = m->encp->cf_file;
1180
0
    return true;
1181
0
}
1182
1183
bool
1184
QPDF::ownerPasswordMatched() const
1185
0
{
1186
0
    return m->encp->owner_password_matched;
1187
0
}
1188
1189
bool
1190
QPDF::userPasswordMatched() const
1191
0
{
1192
0
    return m->encp->user_password_matched;
1193
0
}
1194
1195
bool
1196
QPDF::allowAccessibility()
1197
0
{
1198
0
    return m->encp->R() < 3 ? m->encp->P(5) : m->encp->P(10);
1199
0
}
1200
1201
bool
1202
QPDF::allowExtractAll()
1203
0
{
1204
0
    return m->encp->P(5);
1205
0
}
1206
1207
bool
1208
QPDF::allowPrintLowRes()
1209
0
{
1210
0
    return m->encp->P(3);
1211
0
}
1212
1213
bool
1214
QPDF::allowPrintHighRes()
1215
0
{
1216
0
    return allowPrintLowRes() && (m->encp->R() < 3 ? true : m->encp->P(12));
1217
0
}
1218
1219
bool
1220
QPDF::allowModifyAssembly()
1221
0
{
1222
0
    return m->encp->R() < 3 ? m->encp->P(4) : m->encp->P(11);
1223
0
}
1224
1225
bool
1226
QPDF::allowModifyForm()
1227
0
{
1228
0
    return m->encp->R() < 3 ? m->encp->P(6) : m->encp->P(9);
1229
0
}
1230
1231
bool
1232
QPDF::allowModifyAnnotation()
1233
0
{
1234
0
    return m->encp->P(6);
1235
0
}
1236
1237
bool
1238
QPDF::allowModifyOther()
1239
0
{
1240
0
    return m->encp->P(4);
1241
0
}
1242
1243
bool
1244
QPDF::allowModifyAll()
1245
0
{
1246
0
    return allowModifyAnnotation() && allowModifyOther() &&
1247
0
        (m->encp->R() < 3 ? true : allowModifyForm() && allowModifyAssembly());
1248
0
}