Add FPDFSignatureObj_GetDocMDPPermission()

A document is OK to not contain any explicit markup for this, in which
case 0 is returned. If a permission parameter is found, then the
returned level of 1, 2 or 3 controls if no incremental updates are
allowed (1) or incremental updates are OK, but annotations are not
allowed (2) or even annotations are allowed (3).

Note how there is a difference between omitting just the /P key
(implicitly means 2) and omitting the more of this markup, which will
return 0.

This API is meant to be used in combination with the
FPDF_GetTrailerEnds() API, which allows detecting additional incremental
updates after a signature. For example, if a comment is added after
signing, then Permission=1 would result in an invalid signature, while
Permission=3 just results in a partial signature.

Change-Id: I911e9ddaff3a687729e7fe610c013b2ce3283d36
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/75910
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/fpdfsdk/fpdf_signature.cpp b/fpdfsdk/fpdf_signature.cpp
index b438b8f..975c252 100644
--- a/fpdfsdk/fpdf_signature.cpp
+++ b/fpdfsdk/fpdf_signature.cpp
@@ -157,3 +157,44 @@
 
   return NulTerminateMaybeCopyAndReturnLength(obj->GetString(), buffer, length);
 }
+
+FPDF_EXPORT unsigned int FPDF_CALLCONV
+FPDFSignatureObj_GetDocMDPPermission(FPDF_SIGNATURE signature) {
+  int permission = 0;
+  CPDF_Dictionary* signature_dict = CPDFDictionaryFromFPDFSignature(signature);
+  if (!signature_dict)
+    return permission;
+
+  CPDF_Dictionary* value_dict = signature_dict->GetDictFor("V");
+  if (!value_dict)
+    return permission;
+
+  CPDF_Array* references = value_dict->GetArrayFor("Reference");
+  if (!references)
+    return permission;
+
+  CPDF_ArrayLocker locker(references);
+  for (auto& reference : locker) {
+    CPDF_Dictionary* reference_dict = reference->GetDict();
+    if (!reference_dict)
+      continue;
+
+    ByteString transform_method = reference_dict->GetNameFor("TransformMethod");
+    if (transform_method != "DocMDP")
+      continue;
+
+    CPDF_Dictionary* transform_params =
+        reference_dict->GetDictFor("TransformParams");
+    if (!transform_params)
+      continue;
+
+    // Valid values are 1, 2 and 3; 2 is the default.
+    permission = transform_params->GetIntegerFor("P", 2);
+    if (permission < 1 || permission > 3)
+      permission = 0;
+
+    return permission;
+  }
+
+  return permission;
+}
diff --git a/fpdfsdk/fpdf_signature_embeddertest.cpp b/fpdfsdk/fpdf_signature_embeddertest.cpp
index d2fd9c6..e91d9c2 100644
--- a/fpdfsdk/fpdf_signature_embeddertest.cpp
+++ b/fpdfsdk/fpdf_signature_embeddertest.cpp
@@ -187,3 +187,16 @@
   EXPECT_EQ('x', time_buffer[0]);
   EXPECT_EQ('\0', time_buffer[1]);
 }
+
+TEST_F(FPDFSignatureEmbedderTest, GetDocMDPPermission) {
+  ASSERT_TRUE(OpenDocument("docmdp.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  ASSERT_NE(nullptr, signature);
+
+  // FPDFSignatureObj_GetDocMDPPermission() positive testing.
+  unsigned int permission = FPDFSignatureObj_GetDocMDPPermission(signature);
+  EXPECT_EQ(1U, permission);
+
+  // FPDFSignatureObj_GetDocMDPPermission() negative testing.
+  EXPECT_EQ(0U, FPDFSignatureObj_GetDocMDPPermission(nullptr));
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 7708cba..1142725 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -320,6 +320,7 @@
     // fpdf_signature.h
     CHK(FPDFSignatureObj_GetByteRange);
     CHK(FPDFSignatureObj_GetContents);
+    CHK(FPDFSignatureObj_GetDocMDPPermission);
     CHK(FPDFSignatureObj_GetReason);
     CHK(FPDFSignatureObj_GetSubFilter);
     CHK(FPDFSignatureObj_GetTime);
diff --git a/public/fpdf_signature.h b/public/fpdf_signature.h
index f20d9ad..702b67b 100644
--- a/public/fpdf_signature.h
+++ b/public/fpdf_signature.h
@@ -137,6 +137,17 @@
                          char* buffer,
                          unsigned long length);
 
+// Experimental API.
+// Function: FPDFSignatureObj_GetDocMDPPermission
+//          Get the DocMDP permission of a signature object.
+// Parameters:
+//          signature   -   Handle to the signature object. Returned by
+//                          FPDF_GetSignatureObject().
+// Return value:
+//          Returns the permission (1, 2 or 3) on success, 0 on error.
+FPDF_EXPORT unsigned int FPDF_CALLCONV
+FPDFSignatureObj_GetDocMDPPermission(FPDF_SIGNATURE signature);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/testing/resources/docmdp.in b/testing/resources/docmdp.in
new file mode 100644
index 0000000..a8fe283
--- /dev/null
+++ b/testing/resources/docmdp.in
@@ -0,0 +1,88 @@
+{{header}}
+{{object 1 0}} <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+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
+  /Annots [7 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
+{{object 5 0}} <<
+  /Type /Sig
+  /Reference [
+    <<
+      /Type /SigRef
+      /TransformMethod /DocMDP
+      /TransformParams <<
+        /Type /TransformParams
+        /P 1
+        /V /1.2
+      >>
+    >>
+  ]
+>>
+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
diff --git a/testing/resources/docmdp.pdf b/testing/resources/docmdp.pdf
new file mode 100644
index 0000000..0191a00
--- /dev/null
+++ b/testing/resources/docmdp.pdf
@@ -0,0 +1,102 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+  /Type /Catalog
+  /Pages 2 0 R
+  /AcroForm <<
+    /Fields [7 0 R]
+    /SigFlags 3
+  >>
+>>
+endobj
+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
+  /Annots [7 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
+5 0 obj <<
+  /Type /Sig
+  /Reference [
+    <<
+      /Type /SigRef
+      /TransformMethod /DocMDP
+      /TransformParams <<
+        /Type /TransformParams
+        /P 1
+        /V /1.2
+      >>
+    >>
+  ]
+>>
+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 
+0000000015 00000 n 
+0000000131 00000 n 
+0000000220 00000 n 
+0000000307 00000 n 
+0000000547 00000 n 
+0000000760 00000 n 
+0000000862 00000 n 
+trailer <<
+  /Root 1 0 R
+  /Size 8
+>>
+startxref
+1034
+%%EOF