/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.45M | Pipeline(identifier, next), |
17 | 7.45M | key(key), |
18 | 7.45M | crypto(QPDFCryptoProvider::getImpl()), |
19 | 7.45M | encrypt(encrypt) |
20 | 7.45M | { |
21 | 7.45M | util::assertion(next, "Attempt to create Pl_AES_PDF with nullptr as next"); |
22 | 7.45M | util::no_ci_rt_error_if(!(key.size() == 32 || key.size() == 16), "unsupported key length"); |
23 | 7.45M | std::memset(this->inbuf, 0, this->buf_size); |
24 | 7.45M | std::memset(this->outbuf, 0, this->buf_size); |
25 | 7.45M | std::memset(this->cbc_block, 0, this->buf_size); |
26 | 7.45M | } |
27 | | |
28 | | void |
29 | | Pl_AES_PDF::useZeroIV() |
30 | 65.7k | { |
31 | 65.7k | use_zero_iv = true; |
32 | 65.7k | } |
33 | | |
34 | | void |
35 | | Pl_AES_PDF::disablePadding() |
36 | 7.05M | { |
37 | 7.05M | disable_padding = true; |
38 | 7.05M | } |
39 | | |
40 | | void |
41 | | Pl_AES_PDF::setIV(unsigned char const* iv, size_t bytes) |
42 | 6.98M | { |
43 | 6.98M | util::assertion( |
44 | 6.98M | bytes == buf_size, |
45 | 6.98M | "Pl_AES_PDF: specified initialization vector size in bytes must be " + |
46 | 6.98M | std::to_string(bytes)); |
47 | 6.98M | use_specified_iv = true; |
48 | 6.98M | memcpy(specified_iv, iv, bytes); |
49 | 6.98M | } |
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 | 447M | { |
66 | 447M | size_t bytes_left = len; |
67 | 447M | unsigned char const* p = data; |
68 | | |
69 | 2.81G | while (bytes_left > 0) { |
70 | 2.36G | if (offset == buf_size) { |
71 | 2.03G | flush(false); |
72 | 2.03G | } |
73 | | |
74 | 2.36G | size_t available = buf_size - offset; |
75 | 2.36G | size_t bytes = (bytes_left < available ? bytes_left : available); |
76 | 2.36G | bytes_left -= bytes; |
77 | 2.36G | std::memcpy(inbuf + offset, p, bytes); |
78 | 2.36G | offset += bytes; |
79 | 2.36G | p += bytes; |
80 | 2.36G | } |
81 | 447M | } |
82 | | |
83 | | void |
84 | | Pl_AES_PDF::finish() |
85 | 7.45M | { |
86 | 7.45M | if (encrypt) { |
87 | 7.22M | if (offset == buf_size) { |
88 | 7.05M | flush(false); |
89 | 7.05M | } |
90 | 7.22M | 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 | 178k | unsigned char pad = QIntC::to_uchar(buf_size - offset); |
94 | 178k | memset(inbuf + offset, pad, pad); |
95 | 178k | offset = buf_size; |
96 | 178k | flush(false); |
97 | 178k | } |
98 | 7.22M | } else { |
99 | 236k | 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 | 220k | util::assertion(offset < buf_size, "buffer overflow in AES encryption pipeline"); |
104 | 220k | std::memset(inbuf + offset, 0, buf_size - offset); |
105 | 220k | offset = buf_size; |
106 | 220k | } |
107 | 236k | flush(!disable_padding); |
108 | 236k | } |
109 | 7.45M | crypto->rijndael_finalize(); |
110 | 7.45M | next()->finish(); |
111 | 7.45M | } |
112 | | |
113 | | void |
114 | | Pl_AES_PDF::initializeVector() |
115 | 7.22M | { |
116 | 7.22M | if (use_zero_iv) { |
117 | 1.11M | for (unsigned int i = 0; i < buf_size; ++i) { |
118 | 1.05M | cbc_block[i] = 0; |
119 | 1.05M | } |
120 | 7.16M | } else if (use_specified_iv) { |
121 | 6.98M | std::memcpy(cbc_block, specified_iv, buf_size); |
122 | 6.98M | } 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 | 178k | } else { |
127 | 178k | QUtil::initializeWithRandomBytes(cbc_block, buf_size); |
128 | 178k | } |
129 | 7.22M | } |
130 | | |
131 | | void |
132 | | Pl_AES_PDF::flush(bool strip_padding) |
133 | 2.03G | { |
134 | 2.03G | util::assertion(offset == buf_size, "AES pipeline: flush called when buffer was not full"); |
135 | | |
136 | 2.03G | if (first) { |
137 | 7.45M | first = false; |
138 | 7.45M | bool return_after_init = false; |
139 | 7.45M | if (cbc_mode) { |
140 | 7.45M | if (encrypt) { |
141 | | // Set cbc_block to the initialization vector, and if not zero, write it to the |
142 | | // output stream. |
143 | 7.22M | initializeVector(); |
144 | 7.22M | if (!(use_zero_iv || use_specified_iv)) { |
145 | 178k | next()->write(cbc_block, buf_size); |
146 | 178k | } |
147 | 7.22M | } 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.33k | initializeVector(); |
151 | 229k | } else { |
152 | | // Take the first block of input as the initialization vector. There's nothing to |
153 | | // write at this time. |
154 | 229k | memcpy(cbc_block, inbuf, buf_size); |
155 | 229k | offset = 0; |
156 | 229k | return_after_init = true; |
157 | 229k | } |
158 | 7.45M | } |
159 | 7.45M | crypto->rijndael_init( |
160 | 7.45M | encrypt, |
161 | 7.45M | reinterpret_cast<const unsigned char*>(key.data()), |
162 | 7.45M | key.size(), |
163 | 7.45M | cbc_mode, |
164 | 7.45M | cbc_block); |
165 | 7.45M | if (return_after_init) { |
166 | 229k | return; |
167 | 229k | } |
168 | 7.45M | } |
169 | | |
170 | 2.03G | crypto->rijndael_process(inbuf, outbuf); |
171 | 2.03G | unsigned int bytes = buf_size; |
172 | 2.03G | if (strip_padding) { |
173 | 179k | unsigned char last = outbuf[buf_size - 1]; |
174 | 179k | if (last <= buf_size) { |
175 | 23.8k | bool strip = true; |
176 | 54.3k | for (unsigned int i = 1; i <= last; ++i) { |
177 | 47.6k | if (outbuf[buf_size - i] != last) { |
178 | 17.1k | strip = false; |
179 | 17.1k | break; |
180 | 17.1k | } |
181 | 47.6k | } |
182 | 23.8k | if (strip) { |
183 | 6.67k | bytes -= last; |
184 | 6.67k | } |
185 | 23.8k | } |
186 | 179k | } |
187 | 2.03G | offset = 0; |
188 | 2.03G | next()->write(outbuf, bytes); |
189 | 2.03G | } |