Add FPDFSignatureObj_GetSubFilter() API
This follows the same pattern as FPDFSignatureObj_GetByteRange(), 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
PDF reference gives examples on how the SubFilter should be named, and
all of them are ASCII, so probably UTF-8 or UTF-16 would be an overkill
here.
Change-Id: I3a9b360f219578745ea501cbdfb39a117ca5f2bc
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/71970
Commit-Queue: Miklos V <vmiklos@collabora.co.uk>
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/fpdfsdk/fpdf_signature.cpp b/fpdfsdk/fpdf_signature.cpp
index 8d3a01f..9be14bd 100644
--- a/fpdfsdk/fpdf_signature.cpp
+++ b/fpdfsdk/fpdf_signature.cpp
@@ -102,3 +102,23 @@
return byte_range_len;
}
+
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetSubFilter(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 || !value_dict->KeyExist("SubFilter"))
+ return 0;
+
+ ByteString sub_filter = value_dict->GetNameFor("SubFilter");
+ unsigned long sub_filter_len = sub_filter.GetLength() + 1;
+ if (buffer && length >= sub_filter_len)
+ memcpy(buffer, sub_filter.c_str(), sub_filter_len);
+
+ return sub_filter_len;
+}
diff --git a/fpdfsdk/fpdf_signature_embeddertest.cpp b/fpdfsdk/fpdf_signature_embeddertest.cpp
index 8a93916..5df611a 100644
--- a/fpdfsdk/fpdf_signature_embeddertest.cpp
+++ b/fpdfsdk/fpdf_signature_embeddertest.cpp
@@ -92,3 +92,39 @@
EXPECT_EQ(0, byte_range[0]);
EXPECT_EQ(1, byte_range[1]);
}
+
+TEST_F(FPDFSignatureEmbedderTest, GetSubFilter) {
+ ASSERT_TRUE(OpenDocument("two_signatures.pdf"));
+ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+ EXPECT_NE(nullptr, signature);
+
+ // FPDFSignatureObj_GetSubFilter() positive testing.
+ unsigned long size = FPDFSignatureObj_GetSubFilter(signature, nullptr, 0);
+ const char kExpectedSubFilter[] = "ETSI.CAdES.detached";
+ ASSERT_EQ(sizeof(kExpectedSubFilter), size);
+ std::vector<char> sub_filter(size);
+ ASSERT_EQ(size,
+ FPDFSignatureObj_GetSubFilter(signature, sub_filter.data(), size));
+ ASSERT_EQ(0, memcmp(kExpectedSubFilter, sub_filter.data(), size));
+
+ // FPDFSignatureObj_GetSubFilter() negative testing.
+ ASSERT_EQ(0U, FPDFSignatureObj_GetSubFilter(nullptr, nullptr, 0));
+
+ sub_filter.resize(2);
+ sub_filter[0] = 'x';
+ sub_filter[1] = '\0';
+ size = FPDFSignatureObj_GetSubFilter(signature, sub_filter.data(),
+ sub_filter.size());
+ ASSERT_EQ(sizeof(kExpectedSubFilter), size);
+ EXPECT_EQ('x', sub_filter[0]);
+ EXPECT_EQ('\0', sub_filter[1]);
+}
+
+TEST_F(FPDFSignatureEmbedderTest, GetSubFilterNoKeyExists) {
+ ASSERT_TRUE(OpenDocument("signature_no_sub_filter.pdf"));
+ FPDF_SIGNATURE signature = FPDF_GetSignatureObject(document(), 0);
+ EXPECT_NE(nullptr, signature);
+
+ // FPDFSignatureObj_GetSubFilter() negative testing: no SubFilter
+ ASSERT_EQ(0U, FPDFSignatureObj_GetSubFilter(signature, nullptr, 0));
+}
diff --git a/fpdfsdk/fpdf_view_c_api_test.c b/fpdfsdk/fpdf_view_c_api_test.c
index 67617b5..7aaa1d4 100644
--- a/fpdfsdk/fpdf_view_c_api_test.c
+++ b/fpdfsdk/fpdf_view_c_api_test.c
@@ -319,6 +319,7 @@
// fpdf_signature.h
CHK(FPDFSignatureObj_GetByteRange);
CHK(FPDFSignatureObj_GetContents);
+ CHK(FPDFSignatureObj_GetSubFilter);
CHK(FPDF_GetSignatureCount);
CHK(FPDF_GetSignatureObject);
diff --git a/public/fpdf_signature.h b/public/fpdf_signature.h
index 439226d..1dc582e 100644
--- a/public/fpdf_signature.h
+++ b/public/fpdf_signature.h
@@ -75,6 +75,25 @@
int* buffer,
unsigned long length);
+// Experimental API.
+// Function: FPDFSignatureObj_GetSubFilter
+// Get the encoding of the value of a signature object.
+// Parameters:
+// signature - Handle to the signature object. Returned by
+// FPDF_GetSignatureObject().
+// buffer - The address of a buffer that receives the encoding.
+// 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.
+FPDF_EXPORT unsigned long FPDF_CALLCONV
+FPDFSignatureObj_GetSubFilter(FPDF_SIGNATURE signature,
+ char* buffer,
+ unsigned long length);
+
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
diff --git a/testing/resources/signature_no_sub_filter.in b/testing/resources/signature_no_sub_filter.in
new file mode 100644
index 0000000..5c1d1d8
--- /dev/null
+++ b/testing/resources/signature_no_sub_filter.in
@@ -0,0 +1,100 @@
+{{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
+ % Intentionally no /SubFilter
+ /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
diff --git a/testing/resources/signature_no_sub_filter.pdf b/testing/resources/signature_no_sub_filter.pdf
new file mode 100644
index 0000000..ed305ef
--- /dev/null
+++ b/testing/resources/signature_no_sub_filter.pdf
@@ -0,0 +1,124 @@
+%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
+ % Intentionally no /SubFilter
+ /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
+0000001199 00000 n
+0000001301 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+1473
+%%EOF