Add FPDF_GetSignatureCount() API

This is the first step towards exposing enough information about digital
signatures so that client code (not pdfium itself) can evaluate if they
are valid or not.

Change-Id: I9b913521bd700bfadd8ca02f5c6fe49f7b63e31e
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/70830
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 5bcb9cb..8bfe0ca55 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -122,6 +122,7 @@
     "public/fpdf_progressive.h",
     "public/fpdf_save.h",
     "public/fpdf_searchex.h",
+    "public/fpdf_signature.h",
     "public/fpdf_structtree.h",
     "public/fpdf_sysfontinfo.h",
     "public/fpdf_text.h",
diff --git a/fpdfsdk/BUILD.gn b/fpdfsdk/BUILD.gn
index 081152e..93cfe5c 100644
--- a/fpdfsdk/BUILD.gn
+++ b/fpdfsdk/BUILD.gn
@@ -62,6 +62,7 @@
     "fpdf_progressive.cpp",
     "fpdf_save.cpp",
     "fpdf_searchex.cpp",
+    "fpdf_signature.cpp",
     "fpdf_structtree.cpp",
     "fpdf_sysfontinfo.cpp",
     "fpdf_text.cpp",
@@ -145,6 +146,7 @@
     "fpdf_ppo_embeddertest.cpp",
     "fpdf_save_embeddertest.cpp",
     "fpdf_searchex_embeddertest.cpp",
+    "fpdf_signature_embeddertest.cpp",
     "fpdf_structtree_embeddertest.cpp",
     "fpdf_sysfontinfo_embeddertest.cpp",
     "fpdf_text_embeddertest.cpp",
diff --git a/fpdfsdk/fpdf_signature.cpp b/fpdfsdk/fpdf_signature.cpp
new file mode 100644
index 0000000..0d898ef
--- /dev/null
+++ b/fpdfsdk/fpdf_signature.cpp
@@ -0,0 +1,38 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_signature.h"
+
+#include "core/fpdfapi/parser/cpdf_array.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_document.h"
+#include "fpdfsdk/cpdfsdk_helpers.h"
+
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document) {
+  auto* doc = CPDFDocumentFromFPDFDocument(document);
+  if (!doc)
+    return -1;
+
+  CPDF_Dictionary* root = doc->GetRoot();
+  if (!root)
+    return 0;
+
+  const CPDF_Dictionary* acro_form = root->GetDictFor("AcroForm");
+  if (!acro_form)
+    return 0;
+
+  const CPDF_Array* fields = acro_form->GetArrayFor("Fields");
+  if (!fields)
+    return 0;
+
+  int signature_count = 0;
+  CPDF_ArrayLocker locker(fields);
+  for (const auto& field : locker) {
+    const CPDF_Dictionary* field_dict = field->GetDict();
+    if (field_dict && field_dict->GetNameFor("FT") == "Sig")
+      ++signature_count;
+  }
+
+  return signature_count;
+}
diff --git a/fpdfsdk/fpdf_signature_embeddertest.cpp b/fpdfsdk/fpdf_signature_embeddertest.cpp
new file mode 100644
index 0000000..fb8bb3a
--- /dev/null
+++ b/fpdfsdk/fpdf_signature_embeddertest.cpp
@@ -0,0 +1,21 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "public/fpdf_signature.h"
+#include "testing/embedder_test.h"
+
+class FPDFSignatureEmbedderTest : public EmbedderTest {};
+
+TEST_F(FPDFSignatureEmbedderTest, GetSignatureCount) {
+  EXPECT_TRUE(OpenDocument("two_signatures.pdf"));
+  EXPECT_EQ(2, FPDF_GetSignatureCount(document()));
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetSignatureCountZero) {
+  EXPECT_TRUE(OpenDocument("hello_world.pdf"));
+  EXPECT_EQ(0, FPDF_GetSignatureCount(document()));
+
+  // Provide no document.
+  EXPECT_EQ(-1, FPDF_GetSignatureCount(nullptr));
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index f76d3d2..d04d1f8 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -24,6 +24,7 @@
 #include "public/fpdf_progressive.h"
 #include "public/fpdf_save.h"
 #include "public/fpdf_searchex.h"
+#include "public/fpdf_signature.h"
 #include "public/fpdf_structtree.h"
 #include "public/fpdf_sysfontinfo.h"
 #include "public/fpdf_text.h"
@@ -314,6 +315,9 @@
     CHK(FPDFText_GetCharIndexFromTextIndex);
     CHK(FPDFText_GetTextIndexFromCharIndex);
 
+    // fpdf_signature.h
+    CHK(FPDF_GetSignatureCount);
+
     // fpdf_structtree.h
     CHK(FPDF_StructElement_CountChildren);
     CHK(FPDF_StructElement_GetAltText);
diff --git a/public/fpdf_signature.h b/public/fpdf_signature.h
new file mode 100644
index 0000000..7290bcb
--- /dev/null
+++ b/public/fpdf_signature.h
@@ -0,0 +1,28 @@
+// Copyright 2020 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PUBLIC_FPDF_SIGNATURE_H_
+#define PUBLIC_FPDF_SIGNATURE_H_
+
+// NOLINTNEXTLINE(build/include)
+#include "fpdfview.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif  // __cplusplus
+
+// Experimental API.
+// Function: FPDF_GetSignatureCount
+//          Get total number of signatures in the document.
+// Parameters:
+//          document    -   Handle to document. Returned by FPDF_LoadDocument().
+// Return value:
+//          Total number of signatures in the document on success, -1 on error.
+FPDF_EXPORT int FPDF_CALLCONV FPDF_GetSignatureCount(FPDF_DOCUMENT document);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif  // __cplusplus
+
+#endif  // PUBLIC_FPDF_SIGNATURE_H_
diff --git a/testing/resources/two_signatures.in b/testing/resources/two_signatures.in
new file mode 100644
index 0000000..f18495f
--- /dev/null
+++ b/testing/resources/two_signatures.in
@@ -0,0 +1,155 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+  {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+{{object 5 0}} <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 10 30 10]
+  /Contents <308006092A864886F70D010702A0803080020101>
+  /M (D:20200624093114+02'00')
+>>
+endobj
+{{object 6 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% Second incremental update adds a next signature and update objects once again to refer to it.
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R 10 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+{{object 3 0}} <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R 10 0 R]
+>>
+endobj
+{{object 8 0}} <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 40 50 10]
+  /Contents <308006092A864886F70D010702A080308002010131>
+  /M (D:20200624093118+02'00')
+>>
+endobj
+{{object 9 0}} <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+{{object 10 0}} <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature2)
+  /V 8 0 R
+  /DV 8 0 R
+  /AP <<
+    /N 9 0 R
+  >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/two_signatures.pdf b/testing/resources/two_signatures.pdf
new file mode 100644
index 0000000..9a033d6
--- /dev/null
+++ b/testing/resources/two_signatures.pdf
@@ -0,0 +1,195 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+  /Type /Pages
+  /MediaBox [0 0 200 300]
+  /Count 1
+  /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+  /Length 188
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f 
+0000000015 00000 n 
+0000000068 00000 n 
+0000000157 00000 n 
+0000000226 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 5
+>>
+startxref
+466
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+5 0 obj <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 10 30 10]
+  /Contents <308006092A864886F70D010702A0803080020101>
+  /M (D:20200624093114+02'00')
+>>
+endobj
+6 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature1)
+  /V 5 0 R
+  /DV 5 0 R
+  /AP <<
+    /N 6 0 R
+  >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f 
+0000000726 00000 n 
+0000000068 00000 n 
+0000000835 00000 n 
+0000000226 00000 n 
+0000000998 00000 n 
+0000001201 00000 n 
+0000001303 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+1475
+%%EOF
+
+%% Second incremental update adds a next signature and update objects once again to refer to it.
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R 10 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+3 0 obj <<
+  /Type /Page
+  /Parent 2 0 R
+  /Contents 4 0 R
+  /Annots [7 0 R 10 0 R]
+>>
+endobj
+8 0 obj <<
+  /Type /Sig
+  /Filter /Adobe.PPKMS
+  /SubFilter /ETSI.CAdES.detached
+  /ByteRange [0 40 50 10]
+  /Contents <308006092A864886F70D010702A080308002010131>
+  /M (D:20200624093118+02'00')
+>>
+endobj
+9 0 obj <<
+  /Type /XObject
+  /Subtype /Form
+  /BBox [0 0 0 0]
+  /Length 0
+>>
+stream
+endstream
+endobj
+10 0 obj <<
+  /Type /Annot
+  /Subtype /Widget
+  /FT /Sig
+  /F 132
+  /Rect [0 0 0 0]
+  /P 3 0 R
+  /T (Signature2)
+  /V 8 0 R
+  /DV 8 0 R
+  /AP <<
+    /N 9 0 R
+  >>
+>>
+endobj
+xref
+0 11
+0000000000 65535 f 
+0000001801 00000 n 
+0000000068 00000 n 
+0000001917 00000 n 
+0000000226 00000 n 
+0000000998 00000 n 
+0000001201 00000 n 
+0000001303 00000 n 
+0000002011 00000 n 
+0000002216 00000 n 
+0000002318 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 11
+>>
+startxref
+2491
+%%EOF