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