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