summaryrefslogtreecommitdiff
path: root/xmlsecurity/source/pdfio/pdfdocument.cxx
diff options
context:
space:
mode:
authorMiklos Vajna <vmiklos@collabora.co.uk>2016-11-30 17:00:58 +0100
committerMiklos Vajna <vmiklos@collabora.co.uk>2016-11-30 17:07:51 +0000
commitbef9ba5e062b340b1835db94620f7ea4fa0b123b (patch)
tree69cea539cd6e848f9b7241c9f88a21979d54503b /xmlsecurity/source/pdfio/pdfdocument.cxx
parentc038d88c228bb2f4d0dde88f59ee4b9c6620687e (diff)
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@collabora.co.uk> Tested-by: Jenkins <ci@libreoffice.org>
Diffstat (limited to 'xmlsecurity/source/pdfio/pdfdocument.cxx')
-rw-r--r--xmlsecurity/source/pdfio/pdfdocument.cxx205
1 files changed, 205 insertions, 0 deletions
diff --git a/xmlsecurity/source/pdfio/pdfdocument.cxx b/xmlsecurity/source/pdfio/pdfdocument.cxx
index aeea58d48267..2092369404ac 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;