xref: /aosp_15_r20/external/pdfium/core/fpdfapi/parser/cpdf_crypto_handler.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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 
29 namespace {
30 
31 constexpr char kContentsKey[] = "Contents";
32 constexpr char kTypeKey[] = "Type";
33 
34 }  // namespace
35 
36 // static
IsSignatureDictionary(const CPDF_Dictionary * dictionary)37 bool CPDF_CryptoHandler::IsSignatureDictionary(
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 
EncryptContent(uint32_t objnum,uint32_t gennum,pdfium::span<const uint8_t> source,uint8_t * dest_buf,size_t & dest_size) const48 void 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 
96 struct AESCryptContext {
97   bool m_bIV;
98   uint32_t m_BlockOffset;
99   CRYPT_aes_context m_Context;
100   uint8_t m_Block[16];
101 };
102 
DecryptStart(uint32_t objnum,uint32_t gennum)103 void* 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 
DecryptStream(void * context,pdfium::span<const uint8_t> source,BinaryBuffer & dest_buf)137 bool 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) {
170         CRYPT_AESSetIV(&pContext->m_Context, pContext->m_Block);
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 
DecryptFinish(void * context,BinaryBuffer & dest_buf)188 bool 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 
Decrypt(uint32_t objnum,uint32_t gennum,const ByteString & str)212 ByteString 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 
DecryptGetSize(size_t src_size)222 size_t CPDF_CryptoHandler::DecryptGetSize(size_t src_size) {
223   return m_Cipher == Cipher::kAES ? src_size - 16 : src_size;
224 }
225 
IsCipherAES() const226 bool CPDF_CryptoHandler::IsCipherAES() const {
227   return m_Cipher == Cipher::kAES;
228 }
229 
DecryptObjectTree(RetainPtr<CPDF_Object> object)230 bool CPDF_CryptoHandler::DecryptObjectTree(RetainPtr<CPDF_Object> object) {
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)});
260         walker.SkipWalkIntoCurrentObject();
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 
EncryptGetSize(pdfium::span<const uint8_t> source) const311 size_t CPDF_CryptoHandler::EncryptGetSize(
312     pdfium::span<const uint8_t> source) const {
313   return m_Cipher == Cipher::kAES ? source.size() + 32 : source.size();
314 }
315 
CPDF_CryptoHandler(Cipher cipher,const uint8_t * key,size_t keylen)316 CPDF_CryptoHandler::CPDF_CryptoHandler(Cipher cipher,
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 
332 CPDF_CryptoHandler::~CPDF_CryptoHandler() = default;
333 
PopulateKey(uint32_t objnum,uint32_t gennum,uint8_t * key) const334 void 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 }
344