/src/qpdf/libqpdf/Pl_AES_PDF.cc
Line | Count | Source |
1 | | #include <qpdf/Pl_AES_PDF.hh> |
2 | | |
3 | | #include <qpdf/QIntC.hh> |
4 | | #include <qpdf/QPDFCryptoProvider.hh> |
5 | | #include <qpdf/QUtil.hh> |
6 | | #include <qpdf/Util.hh> |
7 | | |
8 | | #include <cstring> |
9 | | #include <string> |
10 | | |
11 | | using namespace qpdf; |
12 | | |
13 | | bool Pl_AES_PDF::use_static_iv = false; |
14 | | |
15 | | Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, bool encrypt, std::string key) : |
16 | 7.39M | Pipeline(identifier, next), |
17 | 7.39M | key(key), |
18 | 7.39M | crypto(QPDFCryptoProvider::getImpl()), |
19 | 7.39M | encrypt(encrypt) |
20 | 7.39M | { |
21 | 7.39M | util::assertion(next, "Attempt to create Pl_AES_PDF with nullptr as next"); |
22 | 7.39M | util::no_ci_rt_error_if(!(key.size() == 32 || key.size() == 16), "unsupported key length"); |
23 | 7.39M | std::memset(this->inbuf, 0, this->buf_size); |
24 | 7.39M | std::memset(this->outbuf, 0, this->buf_size); |
25 | 7.39M | std::memset(this->cbc_block, 0, this->buf_size); |
26 | 7.39M | } |
27 | | |
28 | | void |
29 | | Pl_AES_PDF::useZeroIV() |
30 | 65.1k | { |
31 | 65.1k | use_zero_iv = true; |
32 | 65.1k | } |
33 | | |
34 | | void |
35 | | Pl_AES_PDF::disablePadding() |
36 | 6.98M | { |
37 | 6.98M | disable_padding = true; |
38 | 6.98M | } |
39 | | |
40 | | void |
41 | | Pl_AES_PDF::setIV(unsigned char const* iv, size_t bytes) |
42 | 6.92M | { |
43 | 6.92M | util::assertion( |
44 | 6.92M | bytes == buf_size, |
45 | 6.92M | "Pl_AES_PDF: specified initialization vector size in bytes must be " + |
46 | 6.92M | std::to_string(bytes)); |
47 | 6.92M | use_specified_iv = true; |
48 | 6.92M | memcpy(specified_iv, iv, bytes); |
49 | 6.92M | } |
50 | | |
51 | | void |
52 | | Pl_AES_PDF::disableCBC() |
53 | 0 | { |
54 | 0 | cbc_mode = false; |
55 | 0 | } |
56 | | |
57 | | void |
58 | | Pl_AES_PDF::useStaticIV() |
59 | 0 | { |
60 | 0 | use_static_iv = true; |
61 | 0 | } |
62 | | |
63 | | void |
64 | | Pl_AES_PDF::write(unsigned char const* data, size_t len) |
65 | 443M | { |
66 | 443M | size_t bytes_left = len; |
67 | 443M | unsigned char const* p = data; |
68 | | |
69 | 2.78G | while (bytes_left > 0) { |
70 | 2.34G | if (offset == buf_size) { |
71 | 2.01G | flush(false); |
72 | 2.01G | } |
73 | | |
74 | 2.34G | size_t available = buf_size - offset; |
75 | 2.34G | size_t bytes = (bytes_left < available ? bytes_left : available); |
76 | 2.34G | bytes_left -= bytes; |
77 | 2.34G | std::memcpy(inbuf + offset, p, bytes); |
78 | 2.34G | offset += bytes; |
79 | 2.34G | p += bytes; |
80 | 2.34G | } |
81 | 443M | } |
82 | | |
83 | | void |
84 | | Pl_AES_PDF::finish() |
85 | 7.39M | { |
86 | 7.39M | if (encrypt) { |
87 | 7.15M | if (offset == buf_size) { |
88 | 6.99M | flush(false); |
89 | 6.99M | } |
90 | 7.15M | if (!disable_padding) { |
91 | | // Pad as described in section 3.5.1 of version 1.7 of the PDF specification, including |
92 | | // providing an entire block of padding if the input was a multiple of 16 bytes. |
93 | 175k | unsigned char pad = QIntC::to_uchar(buf_size - offset); |
94 | 175k | memset(inbuf + offset, pad, pad); |
95 | 175k | offset = buf_size; |
96 | 175k | flush(false); |
97 | 175k | } |
98 | 7.15M | } else { |
99 | 237k | if (offset != buf_size) { |
100 | | // This is never supposed to happen as the output is always supposed to be padded. |
101 | | // However, we have encountered files for which the output is not a multiple of the |
102 | | // block size. In this case, pad with zeroes and hope for the best. |
103 | 221k | util::assertion(offset < buf_size, "buffer overflow in AES encryption pipeline"); |
104 | 221k | std::memset(inbuf + offset, 0, buf_size - offset); |
105 | 221k | offset = buf_size; |
106 | 221k | } |
107 | 237k | flush(!disable_padding); |
108 | 237k | } |
109 | 7.39M | crypto->rijndael_finalize(); |
110 | 7.39M | next()->finish(); |
111 | 7.39M | } |
112 | | |
113 | | void |
114 | | Pl_AES_PDF::initializeVector() |
115 | 7.16M | { |
116 | 7.16M | if (use_zero_iv) { |
117 | 1.10M | for (unsigned int i = 0; i < buf_size; ++i) { |
118 | 1.04M | cbc_block[i] = 0; |
119 | 1.04M | } |
120 | 7.09M | } else if (use_specified_iv) { |
121 | 6.92M | std::memcpy(cbc_block, specified_iv, buf_size); |
122 | 6.92M | } else if (use_static_iv) { |
123 | 0 | for (unsigned int i = 0; i < buf_size; ++i) { |
124 | 0 | cbc_block[i] = static_cast<unsigned char>(14U * (1U + i)); |
125 | 0 | } |
126 | 175k | } else { |
127 | 175k | QUtil::initializeWithRandomBytes(cbc_block, buf_size); |
128 | 175k | } |
129 | 7.16M | } |
130 | | |
131 | | void |
132 | | Pl_AES_PDF::flush(bool strip_padding) |
133 | 2.02G | { |
134 | 2.02G | util::assertion(offset == buf_size, "AES pipeline: flush called when buffer was not full"); |
135 | | |
136 | 2.02G | if (first) { |
137 | 7.39M | first = false; |
138 | 7.39M | bool return_after_init = false; |
139 | 7.39M | if (cbc_mode) { |
140 | 7.39M | if (encrypt) { |
141 | | // Set cbc_block to the initialization vector, and if not zero, write it to the |
142 | | // output stream. |
143 | 7.15M | initializeVector(); |
144 | 7.15M | if (!(use_zero_iv || use_specified_iv)) { |
145 | 175k | next()->write(cbc_block, buf_size); |
146 | 175k | } |
147 | 7.15M | } else if (use_zero_iv || use_specified_iv) { |
148 | | // Initialize vector with zeroes; zero vector was not written to the beginning of |
149 | | // the input file. |
150 | 7.27k | initializeVector(); |
151 | 230k | } else { |
152 | | // Take the first block of input as the initialization vector. There's nothing to |
153 | | // write at this time. |
154 | 230k | memcpy(cbc_block, inbuf, buf_size); |
155 | 230k | offset = 0; |
156 | 230k | return_after_init = true; |
157 | 230k | } |
158 | 7.39M | } |
159 | 7.39M | crypto->rijndael_init( |
160 | 7.39M | encrypt, |
161 | 7.39M | reinterpret_cast<const unsigned char*>(key.data()), |
162 | 7.39M | key.size(), |
163 | 7.39M | cbc_mode, |
164 | 7.39M | cbc_block); |
165 | 7.39M | if (return_after_init) { |
166 | 230k | return; |
167 | 230k | } |
168 | 7.39M | } |
169 | | |
170 | 2.02G | crypto->rijndael_process(inbuf, outbuf); |
171 | 2.02G | unsigned int bytes = buf_size; |
172 | 2.02G | if (strip_padding) { |
173 | 180k | unsigned char last = outbuf[buf_size - 1]; |
174 | 180k | if (last <= buf_size) { |
175 | 24.1k | bool strip = true; |
176 | 55.1k | for (unsigned int i = 1; i <= last; ++i) { |
177 | 48.3k | if (outbuf[buf_size - i] != last) { |
178 | 17.4k | strip = false; |
179 | 17.4k | break; |
180 | 17.4k | } |
181 | 48.3k | } |
182 | 24.1k | if (strip) { |
183 | 6.71k | bytes -= last; |
184 | 6.71k | } |
185 | 24.1k | } |
186 | 180k | } |
187 | 2.02G | offset = 0; |
188 | 2.02G | next()->write(outbuf, bytes); |
189 | 2.02G | } |