Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpdf_crypto_handler.cpp
Go to the documentation of this file.
1// Copyright 2014 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/fpdfapi/parser/cpdf_crypto_handler.h"
8
9#include <time.h>
10
11#include <algorithm>
12#include <stack>
13#include <utility>
14
15#include "constants/form_fields.h"
16#include "core/fdrm/fx_crypt.h"
17#include "core/fpdfapi/parser/cpdf_dictionary.h"
18#include "core/fpdfapi/parser/cpdf_number.h"
19#include "core/fpdfapi/parser/cpdf_object_walker.h"
20#include "core/fpdfapi/parser/cpdf_parser.h"
21#include "core/fpdfapi/parser/cpdf_security_handler.h"
22#include "core/fpdfapi/parser/cpdf_simple_parser.h"
23#include "core/fpdfapi/parser/cpdf_stream.h"
24#include "core/fpdfapi/parser/cpdf_stream_acc.h"
25#include "core/fpdfapi/parser/cpdf_string.h"
26#include "core/fxcrt/check.h"
27#include "core/fxcrt/check_op.h"
28#include "core/fxcrt/fx_memcpy_wrappers.h"
29#include "core/fxcrt/stl_util.h"
30
31namespace {
32
33constexpr char kContentsKey[] = "Contents";
34constexpr char kTypeKey[] = "Type";
35
36} // namespace
37
38// static
40 const CPDF_Dictionary* dictionary) {
41 if (!dictionary)
42 return false;
43 RetainPtr<const CPDF_Object> type_obj =
44 dictionary->GetDirectObjectFor(kTypeKey);
45 if (!type_obj)
47 return type_obj && type_obj->GetString() == pdfium::form_fields::kSig;
48}
49
51 uint32_t objnum,
52 uint32_t gennum,
53 pdfium::span<const uint8_t> source) const {
54 if (m_Cipher == Cipher::kNone) {
55 return DataVector<uint8_t>(source.begin(), source.end());
56 }
57 uint8_t realkey[16];
58 size_t realkeylen = sizeof(realkey);
59 if (m_Cipher != Cipher::kAES || m_KeyLen != 32) {
60 uint8_t key1[32];
61 PopulateKey(objnum, gennum, key1);
62 if (m_Cipher == Cipher::kAES) {
63 fxcrt::Copy(ByteStringView("sAlT").unsigned_span(),
64 pdfium::make_span(key1).subspan(m_KeyLen + 5));
65 }
66 size_t len = m_Cipher == Cipher::kAES ? m_KeyLen + 9 : m_KeyLen + 5;
67 CRYPT_MD5Generate(pdfium::make_span(key1).first(len), realkey);
68 realkeylen = std::min(m_KeyLen + 5, sizeof(realkey));
69 }
70 if (m_Cipher == Cipher::kAES) {
71 CRYPT_AESSetKey(m_pAESContext.get(),
72 m_KeyLen == 32 ? m_EncryptKey.data() : realkey, m_KeyLen);
73
74 constexpr size_t kIVSize = 16;
75 constexpr size_t kPaddingSize = 16;
76 const size_t source_padding_size = source.size() % kPaddingSize;
77 const size_t source_data_size = source.size() - source_padding_size;
78
79 DataVector<uint8_t> dest(kIVSize + source_data_size + kPaddingSize);
80 auto dest_span = pdfium::make_span(dest);
81 auto dest_iv_span = dest_span.first(kIVSize);
82 auto dest_data_span = dest_span.subspan(kIVSize, source_data_size);
83 auto dest_padding_span = dest_span.subspan(kIVSize + source_data_size);
84
85 for (auto& v : dest_iv_span) {
86 v = static_cast<uint8_t>(rand());
87 }
88 CRYPT_AESSetIV(m_pAESContext.get(), dest_iv_span.data());
89
90 CRYPT_AESEncrypt(m_pAESContext.get(), dest_data_span,
91 source.first(source_data_size));
92
93 std::array<uint8_t, kPaddingSize> padding;
94 fxcrt::Copy(source.subspan(source_data_size, source_padding_size), padding);
95 fxcrt::Fill(pdfium::make_span(padding).subspan(source_padding_size),
96 16 - source_padding_size);
97 CRYPT_AESEncrypt(m_pAESContext.get(), dest_padding_span, padding);
98 return dest;
99 }
100 DataVector<uint8_t> dest(source.begin(), source.end());
101 CRYPT_ArcFourCryptBlock(dest, pdfium::make_span(realkey).first(realkeylen));
102 return dest;
103}
104
111
112void* CPDF_CryptoHandler::DecryptStart(uint32_t objnum, uint32_t gennum) {
113 if (m_Cipher == Cipher::kNone)
114 return this;
115
116 if (m_Cipher == Cipher::kAES && m_KeyLen == 32) {
117 AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1);
118 pContext->m_bIV = true;
119 pContext->m_BlockOffset = 0;
120 CRYPT_AESSetKey(&pContext->m_Context, m_EncryptKey.data(), 32);
121 return pContext;
122 }
123 uint8_t key1[48];
124 PopulateKey(objnum, gennum, key1);
125
126 if (m_Cipher == Cipher::kAES) {
127 UNSAFE_TODO(FXSYS_memcpy(key1 + m_KeyLen + 5, "sAlT", 4));
128 }
129
130 uint8_t realkey[16];
131 size_t len = m_Cipher == Cipher::kAES ? m_KeyLen + 9 : m_KeyLen + 5;
132 CRYPT_MD5Generate(pdfium::make_span(key1).first(len), realkey);
133 size_t realkeylen = std::min(m_KeyLen + 5, sizeof(realkey));
134
135 if (m_Cipher == Cipher::kAES) {
136 AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1);
137 pContext->m_bIV = true;
138 pContext->m_BlockOffset = 0;
139 CRYPT_AESSetKey(&pContext->m_Context, realkey, 16);
140 return pContext;
141 }
142 CRYPT_rc4_context* pContext = FX_Alloc(CRYPT_rc4_context, 1);
143 CRYPT_ArcFourSetup(pContext, pdfium::make_span(realkey).first(realkeylen));
144 return pContext;
145}
146
147bool CPDF_CryptoHandler::DecryptStream(void* context,
148 pdfium::span<const uint8_t> source,
149 BinaryBuffer& dest_buf) {
150 if (!context)
151 return false;
152
153 if (m_Cipher == Cipher::kNone) {
154 dest_buf.AppendSpan(source);
155 return true;
156 }
157 if (m_Cipher == Cipher::kRC4) {
158 size_t old_size = dest_buf.GetSize();
159 dest_buf.AppendSpan(source);
160 CRYPT_ArcFourCrypt(
161 static_cast<CRYPT_rc4_context*>(context),
162 dest_buf.GetMutableSpan().subspan(old_size, source.size()));
163 return true;
164 }
165 AESCryptContext* pContext = static_cast<AESCryptContext*>(context);
166 uint32_t src_off = 0;
167 uint32_t src_left = source.size();
168 while (true) {
169 uint32_t copy_size = 16 - pContext->m_BlockOffset;
170 if (copy_size > src_left) {
171 copy_size = src_left;
172 }
173 UNSAFE_TODO(FXSYS_memcpy(pContext->m_Block + pContext->m_BlockOffset,
174 source.data() + src_off, copy_size));
175 src_off += copy_size;
176 src_left -= copy_size;
177 pContext->m_BlockOffset += copy_size;
178 if (pContext->m_BlockOffset == 16) {
179 if (pContext->m_bIV) {
180 CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block);
181 pContext->m_bIV = false;
182 pContext->m_BlockOffset = 0;
183 } else if (src_off < source.size()) {
184 uint8_t block_buf[16];
185 CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block,
186 16);
187 dest_buf.AppendSpan(block_buf);
188 pContext->m_BlockOffset = 0;
189 }
190 }
191 if (!src_left) {
192 break;
193 }
194 }
195 return true;
196}
197
198bool CPDF_CryptoHandler::DecryptFinish(void* context, BinaryBuffer& dest_buf) {
199 if (!context)
200 return false;
201
202 if (m_Cipher == Cipher::kNone)
203 return true;
204
205 if (m_Cipher == Cipher::kRC4) {
206 FX_Free(context);
207 return true;
208 }
209 auto* pContext = static_cast<AESCryptContext*>(context);
210 if (pContext->m_BlockOffset == 16) {
211 uint8_t block_buf[16];
212 CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block, 16);
213 if (block_buf[15] < 16) {
214 dest_buf.AppendSpan(
215 pdfium::make_span(block_buf).first(16 - block_buf[15]));
216 }
217 }
218 FX_Free(pContext);
219 return true;
220}
221
222ByteString CPDF_CryptoHandler::Decrypt(uint32_t objnum,
223 uint32_t gennum,
224 const ByteString& str) {
225 BinaryBuffer dest_buf;
226 void* context = DecryptStart(objnum, gennum);
227 DecryptStream(context, str.unsigned_span(), dest_buf);
228 DecryptFinish(context, dest_buf);
229 return ByteString(ByteStringView(dest_buf.GetSpan()));
230}
231
232size_t CPDF_CryptoHandler::DecryptGetSize(size_t src_size) {
233 return m_Cipher == Cipher::kAES ? src_size - 16 : src_size;
234}
235
237 return m_Cipher == Cipher::kAES;
238}
239
241 if (!object)
242 return false;
243
244 struct MayBeSignature {
245 RetainPtr<const CPDF_Dictionary> parent;
246 RetainPtr<CPDF_Object> contents;
247 };
248
249 std::stack<MayBeSignature> may_be_sign_dictionaries;
250 const uint32_t obj_num = object->GetObjNum();
251 const uint32_t gen_num = object->GetGenNum();
252
253 RetainPtr<CPDF_Object> object_to_decrypt = object;
254 while (object_to_decrypt) {
255 CPDF_NonConstObjectWalker walker(std::move(object_to_decrypt));
256 while (RetainPtr<CPDF_Object> child = walker.GetNext()) {
257 RetainPtr<const CPDF_Dictionary> parent_dict =
258 walker.GetParent() ? walker.GetParent()->GetDict() : nullptr;
259 if (walker.dictionary_key() == kContentsKey &&
260 (parent_dict->KeyExist(kTypeKey) ||
261 parent_dict->KeyExist(pdfium::form_fields::kFT))) {
262 // This object may be contents of signature dictionary.
263 // But now values of 'Type' and 'FT' of dictionary keys are encrypted,
264 // and we can not check this.
265 // Temporary skip it, to prevent signature corruption.
266 // It will be decrypted on next interations, if this is not contents of
267 // signature dictionary.
268 may_be_sign_dictionaries.push(
269 {std::move(parent_dict), std::move(child)});
271 continue;
272 }
273 // Strings decryption.
274 if (child->IsString()) {
275 // TODO(art-snake): Move decryption into the CPDF_String class.
276 CPDF_String* str = child->AsMutableString();
277 str->SetString(Decrypt(obj_num, gen_num, str->GetString()));
278 }
279 // Stream decryption.
280 if (child->IsStream()) {
281 // TODO(art-snake): Move decryption into the CPDF_Stream class.
282 CPDF_Stream* stream = child->AsMutableStream();
283 auto stream_access =
284 pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(stream));
285 stream_access->LoadAllDataRaw();
286
287 if (IsCipherAES() && stream_access->GetSize() < 16) {
288 stream->SetData({});
289 continue;
290 }
291
292 BinaryBuffer decrypted_buf;
293 decrypted_buf.EstimateSize(DecryptGetSize(stream_access->GetSize()));
294
295 void* context = DecryptStart(obj_num, gen_num);
296 bool decrypt_result =
297 DecryptStream(context, stream_access->GetSpan(), decrypted_buf);
298 decrypt_result &= DecryptFinish(context, decrypted_buf);
299 if (decrypt_result) {
300 stream->TakeData(decrypted_buf.DetachBuffer());
301 } else {
302 // Decryption failed, set the stream to empty
303 stream->SetData({});
304 }
305 }
306 }
307 // Signature dictionaries check.
308 while (!may_be_sign_dictionaries.empty()) {
309 auto dict_and_contents = may_be_sign_dictionaries.top();
310 may_be_sign_dictionaries.pop();
311 if (!IsSignatureDictionary(dict_and_contents.parent)) {
312 // This is not signature dictionary. Do decrypt its contents.
313 object_to_decrypt = dict_and_contents.contents;
314 break;
315 }
316 }
317 }
318 return true;
319}
320
322 pdfium::span<const uint8_t> key)
323 : m_KeyLen(std::min<size_t>(key.size(), 32)), m_Cipher(cipher) {
324 DCHECK(cipher != Cipher::kAES || key.size() == 16 || key.size() == 24 ||
325 key.size() == 32);
326 DCHECK(cipher != Cipher::kAES2 || key.size() == 32);
327 DCHECK(cipher != Cipher::kRC4 || (key.size() >= 5 && key.size() <= 16));
328
329 if (m_Cipher != Cipher::kNone) {
330 fxcrt::Copy(key.first(m_KeyLen), m_EncryptKey);
331 }
332 if (m_Cipher == Cipher::kAES) {
333 m_pAESContext.reset(FX_Alloc(CRYPT_aes_context, 1));
334 }
335}
336
338
339void CPDF_CryptoHandler::PopulateKey(uint32_t objnum,
340 uint32_t gennum,
341 uint8_t* key) const {
343 FXSYS_memcpy(key, m_EncryptKey.data(), m_KeyLen);
344 key[m_KeyLen + 0] = (uint8_t)objnum;
345 key[m_KeyLen + 1] = (uint8_t)(objnum >> 8);
346 key[m_KeyLen + 2] = (uint8_t)(objnum >> 16);
347 key[m_KeyLen + 3] = (uint8_t)gennum;
348 key[m_KeyLen + 4] = (uint8_t)(gennum >> 8);
349 });
350}
fxcrt::ByteString ByteString
Definition bytestring.h:180
#define DCHECK
Definition check.h:33
CPDF_CryptoHandler(Cipher cipher, pdfium::span< const uint8_t > key)
bool DecryptObjectTree(RetainPtr< CPDF_Object > object)
static bool IsSignatureDictionary(const CPDF_Dictionary *dictionary)
DataVector< uint8_t > EncryptContent(uint32_t objnum, uint32_t gennum, pdfium::span< const uint8_t > source) const
RetainPtr< const CPDF_Object > GetDirectObjectFor(const ByteString &key) const
std::map< ByteString, RetainPtr< CPDF_Object >, std::less<> > DictMap
const ByteString & dictionary_key() const
void SetString(const ByteString &str) override
ByteString GetString() const override
#define UNSAFE_TODO(...)
fxcrt::ByteStringView ByteStringView
CRYPT_aes_context m_Context