Add FPDFSignatureObj_GetTime() API

This follows the same pattern as FPDFSignatureObj_GetReason(), so the
client has to call this function twice, but allocation of the buffer
happens outside pdfium.

The buffer format is documented to be NUL-terminated ASCII, since the
string is expected to mostly contain just numbers. An alternative would
be to actually parse the string, but the PDF reference does not go into
details: e.g. it does not specify if the timezone part is mandatory or
not. So probably the best is to leave this to the clients.

Change-Id: I68b981eacd352777452ad5fdd397f7220d891196
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/72690
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/fpdfsdk/fpdf_signature.cpp b/fpdfsdk/fpdf_signature.cpp
index 548aa5f..0975330 100644
--- a/fpdfsdk/fpdf_signature.cpp
+++ b/fpdfsdk/fpdf_signature.cpp
@@ -142,3 +142,22 @@
   return Utf16EncodeMaybeCopyAndReturnLength(obj->GetUnicodeText(), buffer,
                                              length);
 }
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetTime(FPDF_SIGNATURE signature,
+                         char* buffer,
+                         unsigned long length) {
+  CPDF_Dictionary* signature_dict = CPDFDictionaryFromFPDFSignature(signature);
+  if (!signature_dict)
+    return 0;
+
+  CPDF_Dictionary* value_dict = signature_dict->GetDictFor("V");
+  if (!value_dict)
+    return 0;
+
+  const CPDF_Object* obj = value_dict->GetObjectFor("M");
+  if (!obj || !obj->IsString())
+    return 0;
+
+  return NulTerminateMaybeCopyAndReturnLength(obj->GetString(), buffer, length);
+}
diff --git a/fpdfsdk/fpdf_signature_embeddertest.cpp b/fpdfsdk/fpdf_signature_embeddertest.cpp
index 9b04230..d2fd9c6 100644
--- a/fpdfsdk/fpdf_signature_embeddertest.cpp
+++ b/fpdfsdk/fpdf_signature_embeddertest.cpp
@@ -160,3 +160,30 @@
   EXPECT_EQ('x', buffer[0]);
   EXPECT_EQ('\0', buffer[1]);
 }
+
+TEST_F(FPDFSignatureEmbedderTest, GetTime) {
+  ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+  FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+  EXPECT_NE(nullptr, signature);
+
+  // FPDFSignatureObj_GetTime() positive testing.
+  unsigned long size = FPDFSignatureObj_GetTime(signature, nullptr, 0);
+  const char kExpectedTime[] = "D:20200624093114+02'00'";
+  ASSERT_EQ(sizeof(kExpectedTime), size);
+  std::vector<char> time_buffer(size);
+  ASSERT_EQ(size,
+            FPDFSignatureObj_GetTime(signature, time_buffer.data(), size));
+  ASSERT_EQ(0, memcmp(kExpectedTime, time_buffer.data(), size));
+
+  // FPDFSignatureObj_GetTime() negative testing.
+  ASSERT_EQ(0U, FPDFSignatureObj_GetTime(nullptr, nullptr, 0));
+
+  time_buffer.resize(2);
+  time_buffer[0] = 'x';
+  time_buffer[1] = '\0';
+  size = FPDFSignatureObj_GetTime(signature, time_buffer.data(),
+                                  time_buffer.size());
+  ASSERT_EQ(sizeof(kExpectedTime), size);
+  EXPECT_EQ('x', time_buffer[0]);
+  EXPECT_EQ('\0', time_buffer[1]);
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index e36ece4..d8190e0 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -322,6 +322,7 @@
     CHK(FPDFSignatureObj_GetContents);
     CHK(FPDFSignatureObj_GetReason);
     CHK(FPDFSignatureObj_GetSubFilter);
+    CHK(FPDFSignatureObj_GetTime);
     CHK(FPDF_GetSignatureCount);
     CHK(FPDF_GetSignatureObject);
 
diff --git a/public/fpdf_signature.h b/public/fpdf_signature.h
index 6f079fb..f20d9ad 100644
--- a/public/fpdf_signature.h
+++ b/public/fpdf_signature.h
@@ -113,6 +113,30 @@
                            void* buffer,
                            unsigned long length);
 
+// Experimental API.
+// Function: FPDFSignatureObj_GetTime
+//          Get the time of signing of a signature object.
+// Parameters:
+//          signature   -   Handle to the signature object. Returned by
+//                          FPDF_GetSignatureObject().
+//          buffer      -   The address of a buffer that receives the time.
+//          length      -   The size, in bytes, of |buffer|.
+// Return value:
+//          Returns the number of bytes in the encoding name (including the
+//          trailing NUL character) on success, 0 on error.
+//
+// The |buffer| is always encoded in 7-bit ASCII. If |length| is less than the
+// returned length, or |buffer| is NULL, |buffer| will not be modified.
+//
+// The format of time is expected to be D:YYYYMMDDHHMMSS+XX'YY', i.e. it's
+// percision is seconds, with timezone information. This value should be used
+// only when the time of signing is not available in the (PKCS#7 binary)
+// signature.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetTime(FPDF_SIGNATURE signature,
+                         char* buffer,
+                         unsigned long length);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus