2016-10-13 10:37:02 +02:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
2017-09-05 22:57:54 +02:00
|
|
|
#include <pdfio/pdfdocument.hxx>
|
2016-10-13 10:37:02 +02:00
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include <rtl/string.hxx>
|
2018-01-03 21:06:56 +01:00
|
|
|
#include <rtl/ustrbuf.hxx>
|
2016-10-13 10:37:02 +02:00
|
|
|
#include <sal/log.hxx>
|
|
|
|
#include <sal/types.h>
|
|
|
|
|
2017-07-09 09:42:01 -04:00
|
|
|
#include <svl/sigstruct.hxx>
|
|
|
|
#include <svl/cryptosign.hxx>
|
2018-01-03 21:06:56 +01:00
|
|
|
#include <vcl/filter/pdfdocument.hxx>
|
2016-11-02 17:57:25 +01:00
|
|
|
|
2016-10-13 10:37:02 +02:00
|
|
|
using namespace com::sun::star;
|
|
|
|
|
|
|
|
namespace xmlsecurity
|
|
|
|
{
|
|
|
|
namespace pdfio
|
|
|
|
{
|
2018-03-21 13:23:58 +01:00
|
|
|
bool ValidateSignature(SvStream& rStream, vcl::filter::PDFObjectElement* pSignature,
|
|
|
|
SignatureInformation& rInformation, bool bLast)
|
2017-03-21 18:03:21 +01:00
|
|
|
{
|
|
|
|
vcl::filter::PDFObjectElement* pValue = pSignature->LookupObject("V");
|
|
|
|
if (!pValue)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no value");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-10 08:38:31 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pContents = dynamic_cast<vcl::filter::PDFHexStringElement*>(pValue->Lookup("Contents"));
|
|
|
|
if (!pContents)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no contents");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-10 08:38:31 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pByteRange = dynamic_cast<vcl::filter::PDFArrayElement*>(pValue->Lookup("ByteRange"));
|
|
|
|
if (!pByteRange || pByteRange->GetElements().size() < 2)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no byte range or too few elements");
|
|
|
|
return false;
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
auto pSubFilter = dynamic_cast<vcl::filter::PDFNameElement*>(pValue->Lookup("SubFilter"));
|
2017-07-09 09:42:01 -04:00
|
|
|
const bool bNonDetached = pSubFilter && pSubFilter->GetValue() == "adbe.pkcs7.sha1";
|
2018-03-21 13:23:58 +01:00
|
|
|
if (!pSubFilter
|
|
|
|
|| (pSubFilter->GetValue() != "adbe.pkcs7.detached" && !bNonDetached
|
|
|
|
&& pSubFilter->GetValue() != "ETSI.CAdES.detached"))
|
2017-03-21 18:03:21 +01:00
|
|
|
{
|
|
|
|
if (!pSubFilter)
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: missing sub-filter");
|
|
|
|
else
|
2018-03-21 13:23:58 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unsupported sub-filter: '"
|
|
|
|
<< pSubFilter->GetValue() << "'");
|
2017-03-21 18:03:21 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Reason / comment / description is optional.
|
|
|
|
auto pReason = dynamic_cast<vcl::filter::PDFHexStringElement*>(pValue->Lookup("Reason"));
|
|
|
|
if (pReason)
|
|
|
|
{
|
|
|
|
// See appendUnicodeTextString() for the export equivalent of this.
|
|
|
|
std::vector<unsigned char> aReason = vcl::filter::PDFDocument::DecodeHexString(pReason);
|
|
|
|
OUStringBuffer aBuffer;
|
|
|
|
sal_uInt16 nByte = 0;
|
|
|
|
for (size_t i = 0; i < aReason.size(); ++i)
|
2016-11-09 15:49:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
if (i % 2 == 0)
|
|
|
|
nByte = aReason[i];
|
|
|
|
else
|
2016-11-09 15:49:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
sal_Unicode nUnicode;
|
|
|
|
nUnicode = (nByte << 8);
|
|
|
|
nUnicode |= aReason[i];
|
|
|
|
aBuffer.append(nUnicode);
|
2016-11-09 15:49:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (!aBuffer.isEmpty())
|
|
|
|
rInformation.ouDescription = aBuffer.makeStringAndClear();
|
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Date: used only when the time of signing is not available in the
|
|
|
|
// signature.
|
|
|
|
auto pM = dynamic_cast<vcl::filter::PDFLiteralStringElement*>(pValue->Lookup("M"));
|
|
|
|
if (pM)
|
|
|
|
{
|
|
|
|
// Example: "D:20161027100104".
|
|
|
|
const OString& rM = pM->GetValue();
|
|
|
|
if (rM.startsWith("D:") && rM.getLength() >= 16)
|
2016-11-09 15:49:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
rInformation.stDateTime.Year = rM.copy(2, 4).toInt32();
|
|
|
|
rInformation.stDateTime.Month = rM.copy(6, 2).toInt32();
|
|
|
|
rInformation.stDateTime.Day = rM.copy(8, 2).toInt32();
|
|
|
|
rInformation.stDateTime.Hours = rM.copy(10, 2).toInt32();
|
|
|
|
rInformation.stDateTime.Minutes = rM.copy(12, 2).toInt32();
|
|
|
|
rInformation.stDateTime.Seconds = rM.copy(14, 2).toInt32();
|
2016-11-09 15:49:35 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Build a list of offset-length pairs, representing the signed bytes.
|
|
|
|
std::vector<std::pair<size_t, size_t>> aByteRanges;
|
|
|
|
size_t nByteRangeOffset = 0;
|
|
|
|
const std::vector<vcl::filter::PDFElement*>& rByteRangeElements = pByteRange->GetElements();
|
|
|
|
for (size_t i = 0; i < rByteRangeElements.size(); ++i)
|
|
|
|
{
|
|
|
|
auto pNumber = dynamic_cast<vcl::filter::PDFNumberElement*>(rByteRangeElements[i]);
|
|
|
|
if (!pNumber)
|
2016-11-09 16:53:51 +01:00
|
|
|
{
|
2018-03-21 13:23:58 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio",
|
|
|
|
"ValidateSignature: signature offset and length has to be a number");
|
2017-03-21 18:03:21 +01:00
|
|
|
return false;
|
2016-11-09 16:53:51 +01:00
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
if (i % 2 == 0)
|
2016-11-09 15:49:35 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
nByteRangeOffset = pNumber->GetValue();
|
|
|
|
continue;
|
2016-11-09 15:49:35 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
size_t nByteRangeLength = pNumber->GetValue();
|
2017-07-13 21:10:52 +02:00
|
|
|
aByteRanges.emplace_back(nByteRangeOffset, nByteRangeLength);
|
2017-03-21 18:03:21 +01:00
|
|
|
}
|
2016-11-09 15:49:35 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// Detect if the byte ranges don't cover everything, but the signature itself.
|
|
|
|
if (aByteRanges.size() < 2)
|
|
|
|
{
|
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: expected 2 byte ranges");
|
|
|
|
return false;
|
2016-10-18 09:21:05 +02:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
if (aByteRanges[0].first != 0)
|
2016-10-18 09:21:05 +02:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: first range start is not 0");
|
|
|
|
return false;
|
2016-11-09 15:49:35 +01:00
|
|
|
}
|
2017-03-21 18:03:21 +01:00
|
|
|
// 2 is the leading "<" and the trailing ">" around the hex string.
|
|
|
|
size_t nSignatureLength = static_cast<size_t>(pContents->GetValue().getLength()) + 2;
|
|
|
|
if (aByteRanges[1].first != (aByteRanges[0].second + nSignatureLength))
|
|
|
|
{
|
2018-03-21 13:23:58 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio",
|
|
|
|
"ValidateSignature: second range start is not the end of the signature");
|
2017-03-21 18:03:21 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
rStream.Seek(STREAM_SEEK_TO_END);
|
|
|
|
size_t nFileEnd = rStream.Tell();
|
|
|
|
if (bLast && (aByteRanges[1].first + aByteRanges[1].second) != nFileEnd)
|
|
|
|
// Second range end is not the end of the file.
|
|
|
|
rInformation.bPartialDocumentSignature = true;
|
2016-11-11 09:01:03 +01:00
|
|
|
|
2017-03-21 18:03:21 +01:00
|
|
|
// At this point there is no obviously missing info to validate the
|
|
|
|
// signature.
|
|
|
|
std::vector<unsigned char> aSignature = vcl::filter::PDFDocument::DecodeHexString(pContents);
|
|
|
|
if (aSignature.empty())
|
2016-11-11 09:01:03 +01:00
|
|
|
{
|
2017-03-21 18:03:21 +01:00
|
|
|
SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: empty contents");
|
2016-11-11 09:01:03 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-03-21 13:23:58 +01:00
|
|
|
return svl::crypto::Signing::Verify(rStream, aByteRanges, bNonDetached, aSignature,
|
|
|
|
rInformation);
|
2016-10-25 11:33:21 +02:00
|
|
|
}
|
2016-10-13 10:37:02 +02:00
|
|
|
|
|
|
|
} // namespace pdfio
|
|
|
|
} // namespace xmlsecurity
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|