Save encrypted files without IDs in CPDFSecurityHandlerEmbedderTests.

Add methods to CPDF_Parser and CPDF_CrossRefTable to strip the /ID entry
from the PDF trailer. Use this in CPDFSecurityHandlerEmbedderTests to
exercise the PDF creation code paths where they attempt to write out new
encryption dictionaries. This uncovered at least 2 bugs in the code, so
some of the new test code is disabled for now, with TODOs to fix them.

Bug: chromium:1032090,pdfium:1440
Change-Id: Ic285e4ed7ff89b3dc9c604f3e51c5345a4f64f27
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/63752
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_cross_ref_table.h b/core/fpdfapi/parser/cpdf_cross_ref_table.h
index c2b4f1e..66a51de 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_table.h
+++ b/core/fpdfapi/parser/cpdf_cross_ref_table.h
@@ -50,8 +50,9 @@
   void AddNormal(uint32_t obj_num, uint16_t gen_num, FX_FILESIZE pos);
   void SetFree(uint32_t obj_num);
 
-  const CPDF_Dictionary* trailer() const { return trailer_.Get(); }
   void SetTrailer(RetainPtr<CPDF_Dictionary> trailer);
+  const CPDF_Dictionary* trailer() const { return trailer_.Get(); }
+  CPDF_Dictionary* GetMutableTrailerForTesting() { return trailer_.Get(); }
 
   const ObjectInfo* GetObjectInfo(uint32_t obj_num) const;
 
diff --git a/core/fpdfapi/parser/cpdf_parser.cpp b/core/fpdfapi/parser/cpdf_parser.cpp
index e4ac195..8cfb6fc 100644
--- a/core/fpdfapi/parser/cpdf_parser.cpp
+++ b/core/fpdfapi/parser/cpdf_parser.cpp
@@ -246,7 +246,7 @@
     return HANDLER_ERROR;
 
   auto pSecurityHandler = pdfium::MakeRetain<CPDF_SecurityHandler>();
-  if (!pSecurityHandler->OnInit(pEncryptDict, GetIDArray(), m_Password))
+  if (!pSecurityHandler->OnInit(pEncryptDict, GetIDArray(), GetPassword()))
     return PASSWORD_ERROR;
 
   m_pSecurityHandler = std::move(pSecurityHandler);
@@ -824,6 +824,10 @@
   return m_CrossRefTable->trailer();
 }
 
+CPDF_Dictionary* CPDF_Parser::GetMutableTrailerForTesting() {
+  return m_CrossRefTable->GetMutableTrailerForTesting();
+}
+
 RetainPtr<CPDF_Dictionary> CPDF_Parser::GetCombinedTrailer() const {
   return m_CrossRefTable->trailer()
              ? ToDictionary(m_CrossRefTable->trailer()->Clone())
diff --git a/core/fpdfapi/parser/cpdf_parser.h b/core/fpdfapi/parser/cpdf_parser.h
index 30d08ea..8259f08 100644
--- a/core/fpdfapi/parser/cpdf_parser.h
+++ b/core/fpdfapi/parser/cpdf_parser.h
@@ -68,6 +68,7 @@
   ByteString GetPassword() const { return m_Password; }
 
   const CPDF_Dictionary* GetTrailer() const;
+  CPDF_Dictionary* GetMutableTrailerForTesting();
 
   // Returns a new trailer which combines the last read trailer with the /Root
   // and /Info from previous ones.
diff --git a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
index 6bab48b..a3cf04b 100644
--- a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
@@ -6,6 +6,9 @@
 #include <string>
 
 #include "build/build_config.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fxcrt/fx_system.h"
 #include "public/cpp/fpdf_scopers.h"
 #include "public/fpdf_edit.h"
@@ -52,6 +55,19 @@
     CloseSavedDocument();
   }
 
+  void RemoveTrailerIdFromDocument() {
+    // This is cheating slightly to avoid a layering violation, since this file
+    // cannot include fpdfsdk/cpdfsdk_helpers.h to get access to
+    // CPDFDocumentFromFPDFDocument().
+    CPDF_Document* doc = reinterpret_cast<CPDF_Document*>((document()));
+    ASSERT_TRUE(doc);
+    CPDF_Parser* parser = doc->GetParser();
+    ASSERT_TRUE(parser);
+    CPDF_Dictionary* trailer = parser->GetMutableTrailerForTesting();
+    ASSERT_TRUE(trailer);
+    ASSERT_TRUE(trailer->RemoveFor("ID"));
+  }
+
  private:
   void VerifyHelloWorldPage(FPDF_PAGE page) {
     ASSERT_TRUE(page);
@@ -180,6 +196,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/pdfium/1440): Password is in the wrong encoding.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion2Latin1) {
@@ -190,6 +216,12 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion3UTF8) {
@@ -200,6 +232,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/pdfium/1440): Password is in the wrong encoding.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion3Latin1) {
@@ -210,6 +252,12 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion5UTF8) {
@@ -220,6 +268,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/1032090): This triggers an MSAN error.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion5Latin1) {
@@ -230,6 +288,18 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/1032090): This triggers an MSAN error.
+  // Fix and enable this code.
+  // TODO(crbug.com/pdfium/1440): Password is in the wrong encoding.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion6UTF8) {
@@ -240,6 +310,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/1032090): This triggers an MSAN error.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion6Latin1) {
@@ -250,6 +330,18 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/1032090): This triggers an MSAN error.
+  // Fix and enable this code.
+  // TODO(crbug.com/pdfium/1440): Password is in the wrong encoding.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion2UTF8) {
@@ -261,6 +353,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/pdfium/1440): Password is in the wrong encoding.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion2Latin1) {
@@ -271,6 +373,12 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion3UTF8) {
@@ -281,6 +389,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/pdfium/1440): Password is in the wrong encoding.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion3Latin1) {
@@ -291,6 +409,12 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion5UTF8) {
@@ -301,6 +425,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/1032090): This triggers an MSAN error.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion5Latin1) {
@@ -311,6 +445,18 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/1032090): This triggers an MSAN error.
+  // Fix and enable this code.
+  // TODO(crbug.com/pdfium/1440): Password is in the wrong encoding.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion6UTF8) {
@@ -321,6 +467,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/1032090): This triggers an MSAN error.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion6Latin1) {
@@ -331,4 +487,16 @@
   EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
   VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
   VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+
+#if 0
+  // TODO(crbug.com/1032090): This triggers an MSAN error.
+  // Fix and enable this code.
+  // TODO(crbug.com/pdfium/1440): Password is in the wrong encoding.
+  // Fix and enable this code.
+  ClearString();
+  RemoveTrailerIdFromDocument();
+  EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
+  VerifySavedHelloWorldDocumentWithPassword(kAgeLatin1);
+  VerifySavedHelloWorldDocumentWithPassword(kAgeUTF8);
+#endif
 }