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