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

Miklos Vajna vmiklos at collabora.co.uk
Wed Nov 30 17:08:19 UTC 2016


 xmlsecurity/Library_xmlsecurity.mk            |    1 
 xmlsecurity/inc/sigstruct.hxx                 |    3 
 xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx |   21 ++
 xmlsecurity/source/pdfio/pdfdocument.cxx      |  205 ++++++++++++++++++++++++++
 4 files changed, 230 insertions(+)

New commits:
commit bef9ba5e062b340b1835db94620f7ea4fa0b123b
Author: Miklos Vajna <vmiklos at collabora.co.uk>
Date:   Wed Nov 30 17:00:58 2016 +0100

    xmlsecurity PDF verify: look for the signingCertificateV2 attribute
    
    This is a required part of the PAdES spec, but so far we only wrote it.
    As a start just expose if the attribute exists or not.
    
    Change-Id: Iae3815f764973a2fd29d72593236c2f484172101
    Reviewed-on: https://gerrit.libreoffice.org/31436
    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 af56123..aee3bcb 100644
--- a/xmlsecurity/Library_xmlsecurity.mk
+++ b/xmlsecurity/Library_xmlsecurity.mk
@@ -82,6 +82,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/inc/sigstruct.hxx b/xmlsecurity/inc/sigstruct.hxx
index ab455d5..c217352 100644
--- a/xmlsecurity/inc/sigstruct.hxx
+++ b/xmlsecurity/inc/sigstruct.hxx
@@ -104,12 +104,15 @@ struct SignatureInformation
     css::uno::Sequence<sal_Int8> aSignatureBytes;
     /// For PDF: digest format, from css::xml::crypto::DigestID
     sal_Int32 nDigestID;
+    /// For PDF: has id-aa-signingCertificateV2 as a signed attribute.
+    bool bHasSigningCertificate;
 
     SignatureInformation( sal_Int32 nId )
     {
         nSecurityId = nId;
         nStatus = css::xml::crypto::SecurityOperationStatus_UNKNOWN;
         nDigestID = 0;
+        bHasSigningCertificate = false;
     }
 };
 
diff --git a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
index dbe3319..8932d6f 100644
--- a/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
+++ b/xmlsecurity/qa/unit/pdfsigning/pdfsigning.cxx
@@ -64,6 +64,7 @@ public:
     void testPDF14LOWin();
     /// Test a PAdES document, signed by LO on Linux.
     void testPDFPAdESGood();
+    void testSigningCertificateAttribute();
 
     CPPUNIT_TEST_SUITE(PDFSigningTest);
     CPPUNIT_TEST(testPDFAdd);
@@ -75,6 +76,7 @@ public:
     CPPUNIT_TEST(testPDF16Add);
     CPPUNIT_TEST(testPDF14LOWin);
     CPPUNIT_TEST(testPDFPAdESGood);
+    CPPUNIT_TEST(testSigningCertificateAttribute);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -322,6 +324,25 @@ void PDFSigningTest::testPDFPAdESGood()
     verify(m_directories.getURLFromSrc(DATA_DIRECTORY) + "good-pades.pdf", 1, "ETSI.CAdES.detached");
 }
 
+void PDFSigningTest::testSigningCertificateAttribute()
+{
+    // Create a new signature.
+    OUString aSourceDir = m_directories.getURLFromSrc(DATA_DIRECTORY);
+    OUString aInURL = aSourceDir + "no.pdf";
+    OUString aTargetDir = m_directories.getURLFromWorkdir("/CppunitTest/xmlsecurity_pdfsigning.test.user/");
+    OUString aOutURL = aTargetDir + "signing-certificate-attribute.pdf";
+    bool bHadCertificates = sign(aInURL, aOutURL, 0);
+    if (!bHadCertificates)
+        return;
+
+    // Verify it.
+    std::vector<SignatureInformation> aInfos = verify(aOutURL, 1, "ETSI.CAdES.detached");
+    CPPUNIT_ASSERT(!aInfos.empty());
+    SignatureInformation& rInformation = aInfos[0];
+    // Assert that it has a signed signingCertificateV2 attribute.
+    CPPUNIT_ASSERT(rInformation.bHasSigningCertificate);
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(PDFSigningTest);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index aeea58d..2092369 100644
--- a/xmlsecurity/source/pdfio/pdfdocument.cxx
+++ b/xmlsecurity/source/pdfio/pdfdocument.cxx
@@ -34,6 +34,7 @@
 #include <cert.h>
 #include <cms.h>
 #include <nss.h>
+#include <secerr.h>
 #include <sechash.h>
 #endif
 
@@ -1936,6 +1937,161 @@ std::vector<unsigned char> PDFDocument::DecodeHexString(PDFHexStringElement* pEl
     return aRet;
 }
 
+#ifdef XMLSEC_CRYPTO_NSS
+namespace
+{
+/// Similar to NSS_CMSAttributeArray_FindAttrByOidTag(), but works directly with a SECOidData.
+NSSCMSAttribute* CMSAttributeArray_FindAttrByOidData(NSSCMSAttribute** attrs, SECOidData* oid, PRBool only)
+{
+    NSSCMSAttribute* attr1, *attr2;
+
+    if (attrs == nullptr)
+        return nullptr;
+
+    if (oid == nullptr)
+        return nullptr;
+
+    while ((attr1 = *attrs++) != nullptr)
+    {
+        if (attr1->type.len == oid->oid.len && PORT_Memcmp(attr1->type.data,
+                oid->oid.data,
+                oid->oid.len) == 0)
+            break;
+    }
+
+    if (attr1 == nullptr)
+        return nullptr;
+
+    if (!only)
+        return attr1;
+
+    while ((attr2 = *attrs++) != nullptr)
+    {
+        if (attr2->type.len == oid->oid.len && PORT_Memcmp(attr2->type.data,
+                oid->oid.data,
+                oid->oid.len) == 0)
+            break;
+    }
+
+    if (attr2 != nullptr)
+        return nullptr;
+
+    return attr1;
+}
+
+/// Same as SEC_StringToOID(), which is private to us.
+SECStatus StringToOID(SECItem* to, const char* from, PRUint32 len)
+{
+    PRUint32 decimal_numbers = 0;
+    PRUint32 result_bytes = 0;
+    SECStatus rv;
+    PRUint8 result[1024];
+
+    static const PRUint32 max_decimal = (0xffffffff / 10);
+    static const char OIDstring[] = {"OID."};
+
+    if (!from || !to)
+    {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    if (!len)
+    {
+        len = PL_strlen(from);
+    }
+    if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4))
+    {
+        from += 4; /* skip leading "OID." if present */
+        len  -= 4;
+    }
+    if (!len)
+    {
+bad_data:
+        PORT_SetError(SEC_ERROR_BAD_DATA);
+        return SECFailure;
+    }
+    do
+    {
+        PRUint32 decimal = 0;
+        while (len > 0 && isdigit(*from))
+        {
+            PRUint32 addend = (*from++ - '0');
+            --len;
+            if (decimal > max_decimal)  /* overflow */
+                goto bad_data;
+            decimal = (decimal * 10) + addend;
+            if (decimal < addend)   /* overflow */
+                goto bad_data;
+        }
+        if (len != 0 && *from != '.')
+        {
+            goto bad_data;
+        }
+        if (decimal_numbers == 0)
+        {
+            if (decimal > 2)
+                goto bad_data;
+            result[0] = decimal * 40;
+            result_bytes = 1;
+        }
+        else if (decimal_numbers == 1)
+        {
+            if (decimal > 40)
+                goto bad_data;
+            result[0] += decimal;
+        }
+        else
+        {
+            /* encode the decimal number,  */
+            PRUint8* rp;
+            PRUint32 num_bytes = 0;
+            PRUint32 tmp = decimal;
+            while (tmp)
+            {
+                num_bytes++;
+                tmp >>= 7;
+            }
+            if (!num_bytes)
+                ++num_bytes;  /* use one byte for a zero value */
+            if (num_bytes + result_bytes > sizeof result)
+                goto bad_data;
+            tmp = num_bytes;
+            rp = result + result_bytes - 1;
+            rp[tmp] = (PRUint8)(decimal & 0x7f);
+            decimal >>= 7;
+            while (--tmp > 0)
+            {
+                rp[tmp] = (PRUint8)(decimal | 0x80);
+                decimal >>= 7;
+            }
+            result_bytes += num_bytes;
+        }
+        ++decimal_numbers;
+        if (len > 0)   /* skip trailing '.' */
+        {
+            ++from;
+            --len;
+        }
+    }
+    while (len > 0);
+    /* now result contains result_bytes of data */
+    if (to->data && to->len >= result_bytes)
+    {
+        PORT_Memcpy(to->data, result, to->len = result_bytes);
+        rv = SECSuccess;
+    }
+    else
+    {
+        SECItem result_item = {siBuffer, nullptr, 0 };
+        result_item.data = result;
+        result_item.len  = result_bytes;
+        rv = SECITEM_CopyItem(nullptr, to, &result_item);
+    }
+    return rv;
+}
+}
+#endif
+
 bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignature, SignatureInformation& rInformation, bool bLast)
 {
     PDFObjectElement* pValue = pSignature->LookupObject("V");
@@ -2235,6 +2391,27 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
         rInformation.stDateTime = aDateTime.GetUNODateTime();
     }
 
+    // Check if we have a signing certificate attribute.
+    SECOidData aOidData;
+    aOidData.oid.data = nullptr;
+    /*
+     * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
+     * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+     *   smime(16) id-aa(2) 47 }
+     */
+    if (StringToOID(&aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess)
+    {
+        SAL_WARN("xmlsecurity.pdfio", "StringToOID() failed");
+        return false;
+    }
+    aOidData.offset = SEC_OID_UNKNOWN;
+    aOidData.desc = "id-aa-signingCertificateV2";
+    aOidData.mechanism = CKM_SHA_1;
+    aOidData.supportedExtension = UNSUPPORTED_CERT_EXTENSION;
+    NSSCMSAttribute* pAttribute = CMSAttributeArray_FindAttrByOidData(pCMSSignerInfo->authAttr, &aOidData, PR_TRUE);
+    if (pAttribute)
+        rInformation.bHasSigningCertificate = true;
+
     SECItem* pContentInfoContentData = pCMSSignedData->contentInfo.content.data;
     if (pContentInfoContentData && pContentInfoContentData->data)
     {
@@ -2389,6 +2566,34 @@ bool PDFDocument::ValidateSignature(SvStream& rStream, PDFObjectElement* pSignat
     if (CryptMsgControl(hMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE, pSignerCertContext->pCertInfo))
         rInformation.nStatus = xml::crypto::SecurityOperationStatus_OPERATION_SUCCEEDED;
 
+    // Check if we have a signing certificate attribute.
+    DWORD nSignedAttributes = 0;
+    if (CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, nullptr, &nSignedAttributes))
+    {
+        std::unique_ptr<BYTE[]> pSignedAttributesBuf(new BYTE[nSignedAttributes]);
+        if (!CryptMsgGetParam(hMsg, CMSG_SIGNER_AUTH_ATTR_PARAM, 0, pSignedAttributesBuf.get(), &nSignedAttributes))
+        {
+            SAL_WARN("xmlsecurity.pdfio", "PDFDocument::ValidateSignature: CryptMsgGetParam() failed");
+            return false;
+        }
+        auto pSignedAttributes = reinterpret_cast<PCRYPT_ATTRIBUTES>(pSignedAttributesBuf.get());
+        for (size_t nAttr = 0; nAttr < pSignedAttributes->cAttr; ++nAttr)
+        {
+            CRYPT_ATTRIBUTE& rAttr = pSignedAttributes->rgAttr[nAttr];
+            /*
+             * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
+             * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
+             *   smime(16) id-aa(2) 47 }
+             */
+            OString aOid("1.2.840.113549.1.9.16.2.47");
+            if (aOid == rAttr.pszObjId)
+            {
+                rInformation.bHasSigningCertificate = true;
+                break;
+            }
+        }
+    }
+
     CertCloseStore(hStoreHandle, CERT_CLOSE_STORE_FORCE_FLAG);
     CryptMsgClose(hMsg);
     return true;


More information about the Libreoffice-commits mailing list