xmlsecurity PDF signing: fix byte range check for multiple signatures
We can mandate that the byte range end is the end of the file for the last signature only. With this, signing a previously unsigned file multiple times works, so add a matching testcase for that as well. Change-Id: I8fe5482890fca4dab8da6305aa7fc7f60df612d8
This commit is contained in:
@@ -88,8 +88,12 @@ public:
|
|||||||
/// Serializes the contents of the edit buffer.
|
/// Serializes the contents of the edit buffer.
|
||||||
bool Write(SvStream& rStream);
|
bool Write(SvStream& rStream);
|
||||||
std::vector<PDFObjectElement*> GetSignatureWidgets();
|
std::vector<PDFObjectElement*> GetSignatureWidgets();
|
||||||
/// Return value is about if we can determine a result, rInformation is about the actual result.
|
/**
|
||||||
static bool ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation);
|
* @param rInformation The actual result.
|
||||||
|
* @param bLast If this is the last signature in the file, so it covers the whole file physically.
|
||||||
|
* @return If we can determinate a result.
|
||||||
|
*/
|
||||||
|
static bool ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation, bool bLast);
|
||||||
/// Remove the nth signature from read document in the edit buffer.
|
/// Remove the nth signature from read document in the edit buffer.
|
||||||
bool RemoveSignature(size_t nPosition);
|
bool RemoveSignature(size_t nPosition);
|
||||||
};
|
};
|
||||||
|
@@ -29,17 +29,25 @@ class PDFSigningTest : public test::BootstrapFixture
|
|||||||
{
|
{
|
||||||
uno::Reference<uno::XComponentContext> mxComponentContext;
|
uno::Reference<uno::XComponentContext> mxComponentContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign rInURL once and save the result as rOutURL, asserting that rInURL
|
||||||
|
* had nOriginalSignatureCount signatures.
|
||||||
|
*/
|
||||||
|
void sign(const OUString& rInURL, const OUString& rOutURL, size_t nOriginalSignatureCount);
|
||||||
public:
|
public:
|
||||||
PDFSigningTest();
|
PDFSigningTest();
|
||||||
void setUp() override;
|
void setUp() override;
|
||||||
|
|
||||||
/// Test adding a new signature to a previously unsigned file.
|
/// Test adding a new signature to a previously unsigned file.
|
||||||
void testPDFAdd();
|
void testPDFAdd();
|
||||||
|
/// Test signing a previously unsigned file twice.
|
||||||
|
void testPDFAdd2();
|
||||||
/// Test remove a signature from a previously signed file.
|
/// Test remove a signature from a previously signed file.
|
||||||
void testPDFRemove();
|
void testPDFRemove();
|
||||||
|
|
||||||
CPPUNIT_TEST_SUITE(PDFSigningTest);
|
CPPUNIT_TEST_SUITE(PDFSigningTest);
|
||||||
CPPUNIT_TEST(testPDFAdd);
|
CPPUNIT_TEST(testPDFAdd);
|
||||||
|
CPPUNIT_TEST(testPDFAdd2);
|
||||||
CPPUNIT_TEST(testPDFRemove);
|
CPPUNIT_TEST(testPDFRemove);
|
||||||
CPPUNIT_TEST_SUITE_END();
|
CPPUNIT_TEST_SUITE_END();
|
||||||
};
|
};
|
||||||
@@ -67,25 +75,20 @@ void PDFSigningTest::setUp()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void PDFSigningTest::testPDFAdd()
|
void PDFSigningTest::sign(const OUString& rInURL, const OUString& rOutURL, size_t nOriginalSignatureCount)
|
||||||
{
|
{
|
||||||
#ifndef _WIN32
|
// Make sure that input has nOriginalSignatureCount signatures.
|
||||||
// Make sure that no.pdf has no signatures.
|
|
||||||
uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(mxComponentContext);
|
uno::Reference<xml::crypto::XSEInitializer> xSEInitializer = xml::crypto::SEInitializer::create(mxComponentContext);
|
||||||
uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
|
uno::Reference<xml::crypto::XXMLSecurityContext> xSecurityContext = xSEInitializer->createSecurityContext(OUString());
|
||||||
xmlsecurity::pdfio::PDFDocument aDocument;
|
xmlsecurity::pdfio::PDFDocument aDocument;
|
||||||
{
|
{
|
||||||
OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
|
SvFileStream aStream(rInURL, StreamMode::READ);
|
||||||
OUString aInURL = aSourceDir + "no.pdf";
|
|
||||||
SvFileStream aStream(aInURL, StreamMode::READ);
|
|
||||||
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
CPPUNIT_ASSERT(aDocument.Read(aStream));
|
||||||
std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
|
std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
|
||||||
CPPUNIT_ASSERT(aSignatures.empty());
|
CPPUNIT_ASSERT_EQUAL(nOriginalSignatureCount, aSignatures.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign it and write out the result as add.pdf.
|
// Sign it and write out the result.
|
||||||
OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/xmlsecurity_signing.test.user/");
|
|
||||||
OUString aOutURL = aTargetDir + "add.pdf";
|
|
||||||
{
|
{
|
||||||
uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
|
uno::Reference<xml::crypto::XSecurityEnvironment> xSecurityEnvironment = xSecurityContext->getSecurityEnvironment();
|
||||||
uno::Sequence<uno::Reference<security::XCertificate>> aCertificates = xSecurityEnvironment->getPersonalCertificates();
|
uno::Sequence<uno::Reference<security::XCertificate>> aCertificates = xSecurityEnvironment->getPersonalCertificates();
|
||||||
@@ -95,21 +98,54 @@ void PDFSigningTest::testPDFAdd()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CPPUNIT_ASSERT(aDocument.Sign(aCertificates[0], "test"));
|
CPPUNIT_ASSERT(aDocument.Sign(aCertificates[0], "test"));
|
||||||
SvFileStream aOutStream(aOutURL, StreamMode::WRITE | StreamMode::TRUNC);
|
SvFileStream aOutStream(rOutURL, StreamMode::WRITE | StreamMode::TRUNC);
|
||||||
CPPUNIT_ASSERT(aDocument.Write(aOutStream));
|
CPPUNIT_ASSERT(aDocument.Write(aOutStream));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read back the signed pdf and make sure that it has one valid signature.
|
// Read back the signed pdf and make sure that it has one valid signature.
|
||||||
{
|
{
|
||||||
SvFileStream aStream(aOutURL, StreamMode::READ);
|
SvFileStream aStream(rOutURL, StreamMode::READ);
|
||||||
xmlsecurity::pdfio::PDFDocument aVerifyDocument;
|
xmlsecurity::pdfio::PDFDocument aVerifyDocument;
|
||||||
CPPUNIT_ASSERT(aVerifyDocument.Read(aStream));
|
CPPUNIT_ASSERT(aVerifyDocument.Read(aStream));
|
||||||
std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aVerifyDocument.GetSignatureWidgets();
|
std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aVerifyDocument.GetSignatureWidgets();
|
||||||
// This was 0 when PDFDocument::Sign() silently returned success, without doing anything.
|
// This was nOriginalSignatureCount when PDFDocument::Sign() silently returned success, without doing anything.
|
||||||
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aSignatures.size());
|
CPPUNIT_ASSERT_EQUAL(nOriginalSignatureCount + 1, aSignatures.size());
|
||||||
SignatureInformation aInfo(0);
|
for (size_t i = 0; i < aSignatures.size(); ++i)
|
||||||
CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[0], aInfo));
|
{
|
||||||
|
SignatureInformation aInfo(i);
|
||||||
|
bool bLast = i == aSignatures.size() - 1;
|
||||||
|
CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo, bLast));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFSigningTest::testPDFAdd()
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
|
||||||
|
OUString aInURL = aSourceDir + "no.pdf";
|
||||||
|
OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/xmlsecurity_signing.test.user/");
|
||||||
|
OUString aOutURL = aTargetDir + "add.pdf";
|
||||||
|
sign(aInURL, aOutURL, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void PDFSigningTest::testPDFAdd2()
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
// Sign.
|
||||||
|
OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
|
||||||
|
OUString aInURL = aSourceDir + "no.pdf";
|
||||||
|
OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/xmlsecurity_signing.test.user/");
|
||||||
|
OUString aOutURL = aTargetDir + "add.pdf";
|
||||||
|
sign(aInURL, aOutURL, 0);
|
||||||
|
|
||||||
|
// Sign again.
|
||||||
|
aInURL = aTargetDir + "add.pdf";
|
||||||
|
aOutURL = aTargetDir + "add2.pdf";
|
||||||
|
// This failed with "second range end is not the end of the file" for the
|
||||||
|
// first signature.
|
||||||
|
sign(aInURL, aOutURL, 1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +164,7 @@ void PDFSigningTest::testPDFRemove()
|
|||||||
std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
|
std::vector<xmlsecurity::pdfio::PDFObjectElement*> aSignatures = aDocument.GetSignatureWidgets();
|
||||||
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aSignatures.size());
|
CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aSignatures.size());
|
||||||
SignatureInformation aInfo(0);
|
SignatureInformation aInfo(0);
|
||||||
CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[0], aInfo));
|
CPPUNIT_ASSERT(xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[0], aInfo, /*bLast=*/true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the signature and write out the result as remove.pdf.
|
// Remove the signature and write out the result as remove.pdf.
|
||||||
|
@@ -58,7 +58,8 @@ bool PDFSignatureHelper::ReadAndVerifySignature(const uno::Reference<io::XInputS
|
|||||||
{
|
{
|
||||||
SignatureInformation aInfo(i);
|
SignatureInformation aInfo(i);
|
||||||
|
|
||||||
if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(*pStream, aSignatures[i], aInfo))
|
bool bLast = i == aSignatures.size() - 1;
|
||||||
|
if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(*pStream, aSignatures[i], aInfo, bLast))
|
||||||
{
|
{
|
||||||
SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
|
SAL_WARN("xmlsecurity.helper", "failed to determine digest match");
|
||||||
continue;
|
continue;
|
||||||
|
@@ -1188,7 +1188,7 @@ std::vector<unsigned char> PDFDocument::DecodeHexString(PDFHexStringElement* pEl
|
|||||||
return aRet;
|
return aRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation)
|
bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation, bool bLast)
|
||||||
{
|
{
|
||||||
PDFObjectElement* pValue = pSignature->LookupObject("V");
|
PDFObjectElement* pValue = pSignature->LookupObject("V");
|
||||||
if (!pValue)
|
if (!pValue)
|
||||||
@@ -1285,7 +1285,7 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
|
|||||||
}
|
}
|
||||||
rStream.Seek(STREAM_SEEK_TO_END);
|
rStream.Seek(STREAM_SEEK_TO_END);
|
||||||
size_t nFileEnd = rStream.Tell();
|
size_t nFileEnd = rStream.Tell();
|
||||||
if ((aByteRanges[1].first + aByteRanges[1].second) != nFileEnd)
|
if (bLast && (aByteRanges[1].first + aByteRanges[1].second) != nFileEnd)
|
||||||
{
|
{
|
||||||
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: second range end is not the end of the file");
|
SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: second range end is not the end of the file");
|
||||||
return false;
|
return false;
|
||||||
|
@@ -113,7 +113,8 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(nArgc, pArgv)
|
|||||||
for (size_t i = 0; i < aSignatures.size(); ++i)
|
for (size_t i = 0; i < aSignatures.size(); ++i)
|
||||||
{
|
{
|
||||||
SignatureInformation aInfo(i);
|
SignatureInformation aInfo(i);
|
||||||
if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo))
|
bool bLast = i == aSignatures.size() - 1;
|
||||||
|
if (!xmlsecurity::pdfio::PDFDocument::ValidateSignature(aStream, aSignatures[i], aInfo, bLast))
|
||||||
{
|
{
|
||||||
SAL_WARN("xmlsecurity.pdfio", "failed to determine digest match");
|
SAL_WARN("xmlsecurity.pdfio", "failed to determine digest match");
|
||||||
return 1;
|
return 1;
|
||||||
|
Reference in New Issue
Block a user