Accept Latin-1 password encoding in document load functions.

BUG=pdfium:1194

Change-Id: Ie25fb194af813ed37d7a91d20777efbef5059a00
Reviewed-on: https://pdfium-review.googlesource.com/c/48213
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fpdfapi/parser/cpdf_security_handler.cpp b/core/fpdfapi/parser/cpdf_security_handler.cpp
index 4e38a74..4909c56 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler.cpp
@@ -397,6 +397,24 @@
 
 bool CPDF_SecurityHandler::CheckPassword(const ByteString& password,
                                          bool bOwner) {
+  if (CheckPasswordImpl(password, bOwner))
+    return true;
+
+  ByteStringView password_view = password.AsStringView();
+  if (password_view.IsASCII())
+    return false;
+
+  if (m_Revision >= 5) {
+    ByteString utf8_password = WideString::FromLatin1(password_view).ToUTF8();
+    return CheckPasswordImpl(utf8_password, bOwner);
+  }
+
+  ByteString latin1_password = WideString::FromUTF8(password_view).ToLatin1();
+  return CheckPasswordImpl(latin1_password, bOwner);
+}
+
+bool CPDF_SecurityHandler::CheckPasswordImpl(const ByteString& password,
+                                             bool bOwner) {
   if (m_Revision >= 5)
     return AES256_CheckPassword(password, bOwner);
 
diff --git a/core/fpdfapi/parser/cpdf_security_handler.h b/core/fpdfapi/parser/cpdf_security_handler.h
index ae37c35..8dcf009 100644
--- a/core/fpdfapi/parser/cpdf_security_handler.h
+++ b/core/fpdfapi/parser/cpdf_security_handler.h
@@ -53,8 +53,8 @@
 
   ByteString GetUserPassword(const ByteString& owner_password) const;
   bool CheckPassword(const ByteString& user_password, bool bOwner);
+  bool CheckPasswordImpl(const ByteString& password, bool bOwner);
   bool CheckUserPassword(const ByteString& password, bool bIgnoreEncryptMeta);
-
   bool CheckOwnerPassword(const ByteString& password);
   bool AES256_CheckPassword(const ByteString& password, bool bOwner);
   void AES256_SetPassword(CPDF_Dictionary* pEncryptDict,
diff --git a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
index bfaacaa..dd30406 100644
--- a/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
+++ b/core/fpdfapi/parser/cpdf_security_handler_embeddertest.cpp
@@ -133,8 +133,8 @@
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion2UTF8) {
   // The password is "age", where the 'a' has a circumflex. Encoding the
-  // password as UTF-8 does not work. TODO(bug_1194): Fix this.
-  ASSERT_FALSE(
+  // password as UTF-8 works.
+  ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r2.pdf", kAgeUTF8));
 }
 
@@ -147,7 +147,7 @@
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion3UTF8) {
   // Same as OwnerPasswordVersion2UTF8 test above.
-  ASSERT_FALSE(
+  ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r3.pdf", kAgeUTF8));
 }
 
@@ -159,36 +159,35 @@
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion5UTF8) {
-  // The password is "age", where the 'a' has a circumflex. Encoding the
-  // password as UTF-8 works.
+  // Same as OwnerPasswordVersion2UTF8 test above.
   ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r5.pdf", kAgeUTF8));
   EXPECT_EQ(5, FPDF_GetSecurityHandlerRevision(document()));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion5Latin1) {
-  // And Latin-1 encoding does not work. TODO(bug_1194): Fix this.
-  ASSERT_FALSE(
+  // Same as OwnerPasswordVersion2Latin1 test above.
+  ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r5.pdf", kAgeLatin1));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion6UTF8) {
-  // Same as OwnerPasswordVersion5UTF8 test above.
+  // Same as OwnerPasswordVersion2UTF8 test above.
   ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r6.pdf", kAgeUTF8));
   EXPECT_EQ(6, FPDF_GetSecurityHandlerRevision(document()));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, OwnerPasswordVersion6Latin1) {
-  // Same as OwnerPasswordVersion5Latin1 test above.
-  ASSERT_FALSE(
+  // Same as OwnerPasswordVersion2Latin1 test above.
+  ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r6.pdf", kAgeLatin1));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion2UTF8) {
   // The password is "hotel", where the 'o' has a circumflex. Encoding the
-  // password as UTF-8 does not work. TODO(bug_1194): Fix this.
-  ASSERT_FALSE(
+  // password as UTF-8 works.
+  ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r2.pdf", kHotelUTF8));
 }
 
@@ -201,7 +200,7 @@
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion3UTF8) {
   // Same as UserPasswordVersion2UTF8 test above.
-  ASSERT_FALSE(
+  ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r3.pdf", kHotelUTF8));
 }
 
@@ -213,28 +212,27 @@
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion5UTF8) {
-  // The password is "hotel", where the 'o' has a circumflex. Encoding the
-  // password as UTF-8 works.
+  // Same as UserPasswordVersion2UTF8 test above.
   ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r5.pdf", kHotelUTF8));
   EXPECT_EQ(5, FPDF_GetSecurityHandlerRevision(document()));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion5Latin1) {
-  // And Latin-1 encoding does not work. TODO(bug_1194): Fix this.
-  ASSERT_FALSE(
+  // Same as UserPasswordVersion2Latin1 test above.
+  ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r5.pdf", kHotelLatin1));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion6UTF8) {
-  // Same as UserPasswordVersion5UTF8 test above.
+  // Same as UserPasswordVersion2UTF8 test above.
   ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r6.pdf", kHotelUTF8));
   EXPECT_EQ(6, FPDF_GetSecurityHandlerRevision(document()));
 }
 
 TEST_F(CPDFSecurityHandlerEmbedderTest, UserPasswordVersion6Latin1) {
-  // Same as UserPasswordVersion5Latin1 test above.
-  ASSERT_FALSE(
+  // Same as UserPasswordVersion2Latin1 test above.
+  ASSERT_TRUE(
       OpenDocumentWithPassword("encrypted_hello_world_r6.pdf", kHotelLatin1));
 }
diff --git a/public/fpdf_dataavail.h b/public/fpdf_dataavail.h
index 68a76a0..ad70b7f 100644
--- a/public/fpdf_dataavail.h
+++ b/public/fpdf_dataavail.h
@@ -119,6 +119,8 @@
 //
 // When FPDFAvail_IsDocAvail() returns TRUE, call FPDFAvail_GetDocument() to
 // retrieve the document handle.
+// See the comments for FPDF_LoadDocument() regarding the encoding for
+// |password|.
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
 FPDFAvail_GetDocument(FPDF_AVAIL avail, FPDF_BYTESTRING password);
 
diff --git a/public/fpdfview.h b/public/fpdfview.h
index 0ba50e2..acd7400 100644
--- a/public/fpdfview.h
+++ b/public/fpdfview.h
@@ -296,12 +296,19 @@
 //          file_path -  Path to the PDF file (including extension).
 //          password  -  A string used as the password for the PDF file.
 //                       If no password is needed, empty or NULL can be used.
+//                       See comments below regarding the encoding.
 // Return value:
 //          A handle to the loaded document, or NULL on failure.
 // Comments:
 //          Loaded document can be closed by FPDF_CloseDocument().
 //          If this function fails, you can use FPDF_GetLastError() to retrieve
 //          the reason why it failed.
+//
+//          The encoding for |password| can be either UTF-8 or Latin-1. PDFs,
+//          depending on the security handler revision, will only accept one or
+//          the other encoding. If |password|'s encoding and the PDF's expected
+//          encoding do not match, FPDF_LoadDocument() will automatically
+//          convert |password| to the other encoding.
 FPDF_EXPORT FPDF_DOCUMENT FPDF_CALLCONV
 FPDF_LoadDocument(FPDF_STRING file_path, FPDF_BYTESTRING password);
 
@@ -319,6 +326,9 @@
 //          The loaded document can be closed by FPDF_CloseDocument.
 //          If this function fails, you can use FPDF_GetLastError() to retrieve
 //          the reason why it failed.
+//
+//          See the comments for FPDF_LoadDocument() regarding the encoding for
+//          |password|.
 // Notes:
 //          If PDFium is built with the XFA module, the application should call
 //          FPDF_LoadXFA() function after the PDF document loaded to support XFA
@@ -447,6 +457,9 @@
 //          document is closed.
 //
 //          The loaded document can be closed with FPDF_CloseDocument.
+//
+//          See the comments for FPDF_LoadDocument() regarding the encoding for
+//          |password|.
 // Notes:
 //          If PDFium is built with the XFA module, the application should call
 //          FPDF_LoadXFA() function after the PDF document loaded to support XFA