[Libreoffice-commits] core.git: xmlsecurity/Library_xmlsecurity.mk xmlsecurity/source

Miklos Vajna vmiklos at collabora.co.uk
Tue Oct 18 18:31:01 UTC 2016


 xmlsecurity/Library_xmlsecurity.mk       |    1 
 xmlsecurity/source/pdfio/pdfdocument.cxx |  206 ++++++++++++++++++++++++++++++-
 2 files changed, 204 insertions(+), 3 deletions(-)

New commits:
commit c70eeb2be7ac0a2c0c81a7cbc9642b0ae824b110
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Tue Oct 18 15:31:28 2016 +0200

    xmlsecurity: use NSS on Linux/macOS to generate pdf signature
    
    This is just the minimum so that our own PDF signature validation is
    happy.
    
    Change-Id: I1148817c11174fd4f9184d0ce2c0511e9d6bd11c
    Reviewed-on: https://gerrit.libreoffice.org/30018
    Reviewed-by: Miklos Vajna <vmiklos at collabora.co.uk>
    Tested-by: Jenkins <ci at libreoffice.org>

diff --git a/xmlsecurity/Library_xmlsecurity.mk b/xmlsecurity/Library_xmlsecurity.mk
index c5e8d68..e211c61 100644
--- a/xmlsecurity/Library_xmlsecurity.mk
+++ b/xmlsecurity/Library_xmlsecurity.mk
@@ -78,6 +78,7 @@ $(eval $(call gb_Library_add_defs,xmlsecurity,\
 ))
 $(eval $(call gb_Library_use_externals,xmlsecurity,\
     nss3 \
+    plc4 \
 ))
 endif # BUILD_TYPE=DESKTOP
 endif
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index 9566083..3b01578 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -232,7 +232,102 @@ PDFDocument::PDFDocument()
 {
 }
 
-bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& /*xCertificate*/)
+#ifdef XMLSEC_CRYPTO_NSS
+static NSSCMSMessage* CreateCMSMessage(PRTime nTime,
+                                       NSSCMSSignedData** ppCMSSignedData,
+                                       NSSCMSSignerInfo** ppCMSSigner,
+                                       CERTCertificate* pCertificate,
+                                       SECItem* pDigest)
+{
+    NSSCMSMessage* pResult = NSS_CMSMessage_Create(nullptr);
+    if (!pResult)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSMessage_Create() failed");
+        return nullptr;
+    }
+
+    *ppCMSSignedData = NSS_CMSSignedData_Create(pResult);
+    if (!*ppCMSSignedData)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignedData_Create() failed");
+        return nullptr;
+    }
+
+    NSSCMSContentInfo* pCMSContentInfo = NSS_CMSMessage_GetContentInfo(pResult);
+    if (NSS_CMSContentInfo_SetContent_SignedData(pResult, pCMSContentInfo, *ppCMSSignedData) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSContentInfo_SetContent_SignedData() failed");
+        return nullptr;
+    }
+
+    pCMSContentInfo = NSS_CMSSignedData_GetContentInfo(*ppCMSSignedData);
+
+    // No detached data.
+    if (NSS_CMSContentInfo_SetContent_Data(pResult, pCMSContentInfo, nullptr, PR_TRUE) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSContentInfo_SetContent_Data() failed");
+        return nullptr;
+    }
+
+    *ppCMSSigner = NSS_CMSSignerInfo_Create(pResult, pCertificate, SEC_OID_SHA1);
+    if (!*ppCMSSigner)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignerInfo_Create() failed");
+        return nullptr;
+    }
+
+    if (NSS_CMSSignerInfo_AddSigningTime(*ppCMSSigner, nTime) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignerInfo_AddSigningTime() failed");
+        return nullptr;
+    }
+
+    if (NSS_CMSSignerInfo_IncludeCerts(*ppCMSSigner, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignerInfo_IncludeCerts() failed");
+        return nullptr;
+    }
+
+    if (NSS_CMSSignedData_AddCertificate(*ppCMSSignedData, pCertificate) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignedData_AddCertificate() failed");
+        return nullptr;
+    }
+
+    if (NSS_CMSSignedData_AddSignerInfo(*ppCMSSignedData, *ppCMSSigner) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignedData_AddSignerInfo() failed");
+        return nullptr;
+    }
+
+    if (NSS_CMSSignedData_SetDigestValue(*ppCMSSignedData, SEC_OID_SHA1, pDigest) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "CreateCMSMessage: NSS_CMSSignedData_SetDigestValue() failed");
+        return nullptr;
+    }
+
+    return pResult;
+}
+
+static char* PasswordCallback(PK11SlotInfo* /*pSlot*/, PRBool /*bRetry*/, void* pArg)
+{
+    return PL_strdup(static_cast<char*>(pArg));
+}
+
+static void AppendHex(sal_Int8 nInt, OStringBuffer& rBuffer)
+{
+    static const sal_Char pHexDigits[] =
+    {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+    rBuffer.append(pHexDigits[(nInt >> 4) & 15]);
+    rBuffer.append(pHexDigits[nInt & 15]);
+}
+
+#endif
+
+bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificate)
 {
     m_aEditBuffer.WriteCharPtr("\n");
 
@@ -357,7 +452,7 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& /*xCertific
 
     // Write the xref table.
     sal_uInt64 nXRefOffset = m_aEditBuffer.Tell();
-    m_aEditBuffer.WriteCharPtr("\nxref\n0 ");
+    m_aEditBuffer.WriteCharPtr("xref\n0 ");
     m_aEditBuffer.WriteUInt32AsString(m_aXRef.size());
     m_aEditBuffer.WriteCharPtr("\n");
     for (size_t nObject = 0; nObject < m_aXRef.size(); ++nObject)
@@ -423,7 +518,109 @@ bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& /*xCertific
     aByteRangeBuffer.append(" ]");
     m_aEditBuffer.WriteOString(aByteRangeBuffer.toString());
 
+    // Create the PKCS#7 object.
+    css::uno::Sequence<sal_Int8> aDerEncoded = xCertificate->getEncoded();
+    if (!aDerEncoded.hasElements())
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: empty certificate");
+        return false;
+    }
+
+    sal_Int8* pDerEncoded = aDerEncoded.getArray();
+    sal_Int32 nDerEncoded = aDerEncoded.getLength();
+
+#ifdef XMLSEC_CRYPTO_NSS
+    CERTCertificate* pCertificate = CERT_DecodeCertFromPackage(reinterpret_cast<char*>(pDerEncoded), nDerEncoded);
+    if (!pCertificate)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: CERT_DecodeCertFromPackage() failed");
+        return false;
+    }
+
+    HASHContext* pHASHContext = HASH_Create(HASH_AlgSHA1);
+    if (!pHASHContext)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: HASH_Create() failed");
+        return false;
+    }
+
+    HASH_Begin(pHASHContext);
+
+    m_aEditBuffer.Seek(0);
+    sal_uInt64 nBufferSize = nSignatureContentOffset - 1;
+    std::unique_ptr<char[]> aBuffer(new char[nBufferSize]);
+    m_aEditBuffer.ReadBytes(aBuffer.get(), nBufferSize);
+    HASH_Update(pHASHContext, reinterpret_cast<const unsigned char*>(aBuffer.get()), nBufferSize);
+
+    m_aEditBuffer.Seek(nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
+    nBufferSize = nLastByteRangeLength;
+    aBuffer.reset(new char[nBufferSize]);
+    m_aEditBuffer.ReadBytes(aBuffer.get(), nBufferSize);
+    HASH_Update(pHASHContext, reinterpret_cast<const unsigned char*>(aBuffer.get()), nBufferSize);
+
+    SECItem aDigestItem;
+    unsigned char aDigest[SHA1_LENGTH];
+    aDigestItem.data = aDigest;
+    HASH_End(pHASHContext, aDigestItem.data, &aDigestItem.len, SHA1_LENGTH);
+    HASH_Destroy(pHASHContext);
+
+    PRTime nNow = PR_Now();
+    NSSCMSSignedData* pCMSSignedData;
+    NSSCMSSignerInfo* pCMSSignerInfo;
+    NSSCMSMessage* pCMSMessage = CreateCMSMessage(nNow, &pCMSSignedData, &pCMSSignerInfo, pCertificate, &aDigestItem);
+    if (!pCMSMessage)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: CreateCMSMessage() failed");
+        return false;
+    }
+
+    char* pPass = strdup("");
+    SECItem aCMSOutputItem;
+    aCMSOutputItem.data = nullptr;
+    aCMSOutputItem.len = 0;
+    PLArenaPool* pAreanaPool = PORT_NewArena(10000);
+    NSSCMSEncoderContext* pCMSEncoderContext;
+
+    pCMSEncoderContext = NSS_CMSEncoder_Start(pCMSMessage, nullptr, nullptr, &aCMSOutputItem, pAreanaPool, PasswordCallback, pPass, nullptr, nullptr, nullptr, nullptr);
+
+    if (!pCMSEncoderContext)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: NSS_CMSEncoder_Start() failed");
+        return false;
+    }
+
+    if (NSS_CMSEncoder_Finish(pCMSEncoderContext) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: NSS_CMSEncoder_Finish() failed");
+        return false;
+    }
+
+    free(pPass);
+
+    if (aCMSOutputItem.len * 2 > MAX_SIGNATURE_CONTENT_LENGTH)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "PDFDocument::Sign: not enough space to write the signature");
+        return false;
+    }
+
+    OStringBuffer aCMSHexBuffer;
+    for (unsigned int i = 0; i < aCMSOutputItem.len; ++i)
+        AppendHex(aCMSOutputItem.data[i], aCMSHexBuffer);
+    assert(aCMSHexBuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH);
+
+    m_aEditBuffer.Seek(nSignatureContentOffset);
+    m_aEditBuffer.WriteOString(aCMSHexBuffer.toString());
+
+    NSS_CMSMessage_Destroy(pCMSMessage);
+
     return true;
+#endif
+
+    // Not implemented.
+    (void)pDerEncoded;
+    (void)nDerEncoded;
+
+    return false;
 }
 
 bool PDFDocument::Write(SvStream& rStream)
@@ -1667,8 +1864,11 @@ double PDFReferenceElement::LookupNumber(SvStream& rStream) const
 PDFObjectElement* PDFReferenceElement::LookupObject() const
 {
     const std::vector< std::unique_ptr<PDFElement> >& rElements = m_rDoc.GetElements();
-    for (const auto& rElement : rElements)
+    // Iterate in reverse order, so in case an incremental update adds a newer
+    // version, we find it.
+    for (int i = rElements.size() - 1; i >= 0; --i)
     {
+        const std::unique_ptr<PDFElement>& rElement = rElements[i];
         auto* pObjectElement = dynamic_cast<PDFObjectElement*>(rElement.get());
         if (!pObjectElement)
             continue;


More information about the Libreoffice-commits mailing list