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 "third_party/base/check.h"
27#include "third_party/base/check_op.h"
28
29namespace {
30
31constexpr char kContentsKey[] = "Contents";
32constexpr char kTypeKey[] = "Type";
33
34} // namespace
35
36// static
38 const CPDF_Dictionary* dictionary) {
39 if (!dictionary)
40 return false;
41 RetainPtr<const CPDF_Object> type_obj =
42 dictionary->GetDirectObjectFor(kTypeKey);
43 if (!type_obj)
44 type_obj = dictionary->GetDirectObjectFor(pdfium::form_fields::kFT);
45 return type_obj && type_obj->GetString() == pdfium::form_fields::kSig;
46}
47
48void CPDF_CryptoHandler::EncryptContent(uint32_t objnum,
49 uint32_t gennum,
50 pdfium::span<const uint8_t> source,
51 uint8_t* dest_buf,
52 size_t& dest_size) const {
53 if (m_Cipher == Cipher::kNone) {
54 memcpy(dest_buf, source.data(), source.size());
55 return;
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
63 if (m_Cipher == Cipher::kAES)
64 memcpy(key1 + m_KeyLen + 5, "sAlT", 4);
65 size_t len = m_Cipher == Cipher::kAES ? m_KeyLen + 9 : m_KeyLen + 5;
66 CRYPT_MD5Generate({key1, len}, realkey);
67 realkeylen = std::min(m_KeyLen + 5, sizeof(realkey));
68 }
69 if (m_Cipher == Cipher::kAES) {
70 CRYPT_AESSetKey(m_pAESContext.get(),
71 m_KeyLen == 32 ? m_EncryptKey : realkey, m_KeyLen);
72 uint8_t iv[16];
73 for (int i = 0; i < 16; i++) {
74 iv[i] = (uint8_t)rand();
75 }
76 CRYPT_AESSetIV(m_pAESContext.get(), iv);
77 memcpy(dest_buf, iv, 16);
78 int nblocks = source.size() / 16;
79 CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + 16, source.data(),
80 nblocks * 16);
81 uint8_t padding[16];
82 memcpy(padding, source.data() + nblocks * 16, source.size() % 16);
83 memset(padding + source.size() % 16, 16 - source.size() % 16,
84 16 - source.size() % 16);
85 CRYPT_AESEncrypt(m_pAESContext.get(), dest_buf + nblocks * 16 + 16, padding,
86 16);
87 dest_size = 32 + nblocks * 16;
88 } else {
89 DCHECK_EQ(dest_size, source.size());
90 if (dest_buf != source.data())
91 memcpy(dest_buf, source.data(), source.size());
92 CRYPT_ArcFourCryptBlock({dest_buf, dest_size}, {realkey, realkeylen});
93 }
94}
95
102
103void* CPDF_CryptoHandler::DecryptStart(uint32_t objnum, uint32_t gennum) {
104 if (m_Cipher == Cipher::kNone)
105 return this;
106
107 if (m_Cipher == Cipher::kAES && m_KeyLen == 32) {
108 AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1);
109 pContext->m_bIV = true;
110 pContext->m_BlockOffset = 0;
111 CRYPT_AESSetKey(&pContext->m_Context, m_EncryptKey, 32);
112 return pContext;
113 }
114 uint8_t key1[48];
115 PopulateKey(objnum, gennum, key1);
116
117 if (m_Cipher == Cipher::kAES)
118 memcpy(key1 + m_KeyLen + 5, "sAlT", 4);
119
120 uint8_t realkey[16];
121 size_t len = m_Cipher == Cipher::kAES ? m_KeyLen + 9 : m_KeyLen + 5;
122 CRYPT_MD5Generate({key1, len}, realkey);
123 size_t realkeylen = std::min(m_KeyLen + 5, sizeof(realkey));
124
125 if (m_Cipher == Cipher::kAES) {
126 AESCryptContext* pContext = FX_Alloc(AESCryptContext, 1);
127 pContext->m_bIV = true;
128 pContext->m_BlockOffset = 0;
129 CRYPT_AESSetKey(&pContext->m_Context, realkey, 16);
130 return pContext;
131 }
132 CRYPT_rc4_context* pContext = FX_Alloc(CRYPT_rc4_context, 1);
133 CRYPT_ArcFourSetup(pContext, {realkey, realkeylen});
134 return pContext;
135}
136
137bool CPDF_CryptoHandler::DecryptStream(void* context,
138 pdfium::span<const uint8_t> source,
139 BinaryBuffer& dest_buf) {
140 if (!context)
141 return false;
142
143 if (m_Cipher == Cipher::kNone) {
144 dest_buf.AppendSpan(source);
145 return true;
146 }
147 if (m_Cipher == Cipher::kRC4) {
148 size_t old_size = dest_buf.GetSize();
149 dest_buf.AppendSpan(source);
150 CRYPT_ArcFourCrypt(
151 static_cast<CRYPT_rc4_context*>(context),
152 dest_buf.GetMutableSpan().subspan(old_size, source.size()));
153 return true;
154 }
155 AESCryptContext* pContext = static_cast<AESCryptContext*>(context);
156 uint32_t src_off = 0;
157 uint32_t src_left = source.size();
158 while (true) {
159 uint32_t copy_size = 16 - pContext->m_BlockOffset;
160 if (copy_size > src_left) {
161 copy_size = src_left;
162 }
163 memcpy(pContext->m_Block + pContext->m_BlockOffset, source.data() + src_off,
164 copy_size);
165 src_off += copy_size;
166 src_left -= copy_size;
167 pContext->m_BlockOffset += copy_size;
168 if (pContext->m_BlockOffset == 16) {
169 if (pContext->m_bIV) {
171 pContext->m_bIV = false;
172 pContext->m_BlockOffset = 0;
173 } else if (src_off < source.size()) {
174 uint8_t block_buf[16];
175 CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block,
176 16);
177 dest_buf.AppendSpan(block_buf);
178 pContext->m_BlockOffset = 0;
179 }
180 }
181 if (!src_left) {
182 break;
183 }
184 }
185 return true;
186}
187
188bool CPDF_CryptoHandler::DecryptFinish(void* context, BinaryBuffer& dest_buf) {
189 if (!context)
190 return false;
191
192 if (m_Cipher == Cipher::kNone)
193 return true;
194
195 if (m_Cipher == Cipher::kRC4) {
196 FX_Free(context);
197 return true;
198 }
199 auto* pContext = static_cast<AESCryptContext*>(context);
200 if (pContext->m_BlockOffset == 16) {
201 uint8_t block_buf[16];
202 CRYPT_AESDecrypt(&pContext->m_Context, block_buf, pContext->m_Block, 16);
203 if (block_buf[15] < 16) {
204 dest_buf.AppendSpan(
205 pdfium::make_span(block_buf).first(16 - block_buf[15]));
206 }
207 }
208 FX_Free(pContext);
209 return true;
210}
211
212ByteString CPDF_CryptoHandler::Decrypt(uint32_t objnum,
213 uint32_t gennum,
214 const ByteString& str) {
215 BinaryBuffer dest_buf;
216 void* context = DecryptStart(objnum, gennum);
217 DecryptStream(context, str.raw_span(), dest_buf);
218 DecryptFinish(context, dest_buf);
219 return ByteString(dest_buf.GetSpan());
220}
221
222size_t CPDF_CryptoHandler::DecryptGetSize(size_t src_size) {
223 return m_Cipher == Cipher::kAES ? src_size - 16 : src_size;
224}
225
227 return m_Cipher == Cipher::kAES;
228}
229
231 if (!object)
232 return false;
233
234 struct MayBeSignature {
235 RetainPtr<const CPDF_Dictionary> parent;
236 RetainPtr<CPDF_Object> contents;
237 };
238
239 std::stack<MayBeSignature> may_be_sign_dictionaries;
240 const uint32_t obj_num = object->GetObjNum();
241 const uint32_t gen_num = object->GetGenNum();
242
243 RetainPtr<CPDF_Object> object_to_decrypt = object;
244 while (object_to_decrypt) {
245 CPDF_NonConstObjectWalker walker(std::move(object_to_decrypt));
246 while (RetainPtr<CPDF_Object> child = walker.GetNext()) {
247 RetainPtr<const CPDF_Dictionary> parent_dict =
248 walker.GetParent() ? walker.GetParent()->GetDict() : nullptr;
249 if (walker.dictionary_key() == kContentsKey &&
250 (parent_dict->KeyExist(kTypeKey) ||
251 parent_dict->KeyExist(pdfium::form_fields::kFT))) {
252 // This object may be contents of signature dictionary.
253 // But now values of 'Type' and 'FT' of dictionary keys are encrypted,
254 // and we can not check this.
255 // Temporary skip it, to prevent signature corruption.
256 // It will be decrypted on next interations, if this is not contents of
257 // signature dictionary.
258 may_be_sign_dictionaries.push(
259 {std::move(parent_dict), std::move(child)});
261 continue;
262 }
263 // Strings decryption.
264 if (child->IsString()) {
265 // TODO(art-snake): Move decryption into the CPDF_String class.
266 CPDF_String* str = child->AsMutableString();
267 str->SetString(Decrypt(obj_num, gen_num, str->GetString()));
268 }
269 // Stream decryption.
270 if (child->IsStream()) {
271 // TODO(art-snake): Move decryption into the CPDF_Stream class.
272 CPDF_Stream* stream = child->AsMutableStream();
273 auto stream_access =
274 pdfium::MakeRetain<CPDF_StreamAcc>(pdfium::WrapRetain(stream));
275 stream_access->LoadAllDataRaw();
276
277 if (IsCipherAES() && stream_access->GetSize() < 16) {
278 stream->SetData({});
279 continue;
280 }
281
282 BinaryBuffer decrypted_buf;
283 decrypted_buf.EstimateSize(DecryptGetSize(stream_access->GetSize()));
284
285 void* context = DecryptStart(obj_num, gen_num);
286 bool decrypt_result =
287 DecryptStream(context, stream_access->GetSpan(), decrypted_buf);
288 decrypt_result &= DecryptFinish(context, decrypted_buf);
289 if (decrypt_result) {
290 stream->TakeData(decrypted_buf.DetachBuffer());
291 } else {
292 // Decryption failed, set the stream to empty
293 stream->SetData({});
294 }
295 }
296 }
297 // Signature dictionaries check.
298 while (!may_be_sign_dictionaries.empty()) {
299 auto dict_and_contents = may_be_sign_dictionaries.top();
300 may_be_sign_dictionaries.pop();
301 if (!IsSignatureDictionary(dict_and_contents.parent)) {
302 // This is not signature dictionary. Do decrypt its contents.
303 object_to_decrypt = dict_and_contents.contents;
304 break;
305 }
306 }
307 }
308 return true;
309}
310
312 pdfium::span<const uint8_t> source) const {
313 return m_Cipher == Cipher::kAES ? source.size() + 32 : source.size();
314}
315
317 const uint8_t* key,
318 size_t keylen)
319 : m_KeyLen(std::min<size_t>(keylen, 32)), m_Cipher(cipher) {
320 DCHECK(cipher != Cipher::kAES || keylen == 16 || keylen == 24 ||
321 keylen == 32);
322 DCHECK(cipher != Cipher::kAES2 || keylen == 32);
323 DCHECK(cipher != Cipher::kRC4 || (keylen >= 5 && keylen <= 16));
324
325 if (m_Cipher != Cipher::kNone)
326 memcpy(m_EncryptKey, key, m_KeyLen);
327
328 if (m_Cipher == Cipher::kAES)
329 m_pAESContext.reset(FX_Alloc(CRYPT_aes_context, 1));
330}
331
333
334void CPDF_CryptoHandler::PopulateKey(uint32_t objnum,
335 uint32_t gennum,
336 uint8_t* key) const {
337 memcpy(key, m_EncryptKey, m_KeyLen);
338 key[m_KeyLen + 0] = (uint8_t)objnum;
339 key[m_KeyLen + 1] = (uint8_t)(objnum >> 8);
340 key[m_KeyLen + 2] = (uint8_t)(objnum >> 16);
341 key[m_KeyLen + 3] = (uint8_t)gennum;
342 key[m_KeyLen + 4] = (uint8_t)(gennum >> 8);
343}
bool DecryptObjectTree(RetainPtr< CPDF_Object > object)
static bool IsSignatureDictionary(const CPDF_Dictionary *dictionary)
CPDF_CryptoHandler(Cipher cipher, const uint8_t *key, size_t keylen)
size_t EncryptGetSize(pdfium::span< const uint8_t > source) const
void EncryptContent(uint32_t objnum, uint32_t gennum, pdfium::span< const uint8_t > source, uint8_t *dest_buf, size_t &dest_size) const
RetainPtr< const CPDF_Object > GetDirectObjectFor(const ByteString &key) const
const ByteString & dictionary_key() const
void SetData(pdfium::span< const uint8_t > pData)
void SetString(const ByteString &str) override
ByteString GetString() const override
void AppendSpan(pdfium::span< const uint8_t > span)
pdfium::span< const uint8_t > GetSpan() const
pdfium::span< uint8_t > GetMutableSpan()
void CRYPT_AESSetIV(CRYPT_aes_context *context, const uint8_t *iv)
void CRYPT_AESSetKey(CRYPT_aes_context *context, const uint8_t *key, uint32_t keylen)
void CRYPT_AESDecrypt(CRYPT_aes_context *context, uint8_t *dest, const uint8_t *src, uint32_t size)
CRYPT_aes_context m_Context