1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <pdfio/pdfdocument.hxx>
15 #include <rtl/string.hxx>
16 #include <rtl/ustrbuf.hxx>
17 #include <sal/log.hxx>
18 #include <sal/types.h>
20 #include <svl/sigstruct.hxx>
21 #include <svl/cryptosign.hxx>
22 #include <vcl/filter/pdfdocument.hxx>
24 using namespace com::sun::star
;
30 bool ValidateSignature(SvStream
& rStream
, vcl::filter::PDFObjectElement
* pSignature
,
31 SignatureInformation
& rInformation
, bool bLast
)
33 vcl::filter::PDFObjectElement
* pValue
= pSignature
->LookupObject("V");
36 SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no value");
40 auto pContents
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(pValue
->Lookup("Contents"));
43 SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no contents");
47 auto pByteRange
= dynamic_cast<vcl::filter::PDFArrayElement
*>(pValue
->Lookup("ByteRange"));
48 if (!pByteRange
|| pByteRange
->GetElements().size() < 2)
50 SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: no byte range or too few elements");
54 auto pSubFilter
= dynamic_cast<vcl::filter::PDFNameElement
*>(pValue
->Lookup("SubFilter"));
55 const bool bNonDetached
= pSubFilter
&& pSubFilter
->GetValue() == "adbe.pkcs7.sha1";
57 || (pSubFilter
->GetValue() != "adbe.pkcs7.detached" && !bNonDetached
58 && pSubFilter
->GetValue() != "ETSI.CAdES.detached"))
61 SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: missing sub-filter");
63 SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: unsupported sub-filter: '"
64 << pSubFilter
->GetValue() << "'");
68 // Reason / comment / description is optional.
69 auto pReason
= dynamic_cast<vcl::filter::PDFHexStringElement
*>(pValue
->Lookup("Reason"));
72 // See appendUnicodeTextString() for the export equivalent of this.
73 std::vector
<unsigned char> aReason
= vcl::filter::PDFDocument::DecodeHexString(pReason
);
74 OUStringBuffer aBuffer
;
76 for (size_t i
= 0; i
< aReason
.size(); ++i
)
83 nUnicode
= (nByte
<< 8);
84 nUnicode
|= aReason
[i
];
85 aBuffer
.append(nUnicode
);
89 if (!aBuffer
.isEmpty())
90 rInformation
.ouDescription
= aBuffer
.makeStringAndClear();
93 // Date: used only when the time of signing is not available in the
95 auto pM
= dynamic_cast<vcl::filter::PDFLiteralStringElement
*>(pValue
->Lookup("M"));
98 // Example: "D:20161027100104".
99 const OString
& rM
= pM
->GetValue();
100 if (rM
.startsWith("D:") && rM
.getLength() >= 16)
102 rInformation
.stDateTime
.Year
= rM
.copy(2, 4).toInt32();
103 rInformation
.stDateTime
.Month
= rM
.copy(6, 2).toInt32();
104 rInformation
.stDateTime
.Day
= rM
.copy(8, 2).toInt32();
105 rInformation
.stDateTime
.Hours
= rM
.copy(10, 2).toInt32();
106 rInformation
.stDateTime
.Minutes
= rM
.copy(12, 2).toInt32();
107 rInformation
.stDateTime
.Seconds
= rM
.copy(14, 2).toInt32();
111 // Build a list of offset-length pairs, representing the signed bytes.
112 std::vector
<std::pair
<size_t, size_t>> aByteRanges
;
113 size_t nByteRangeOffset
= 0;
114 const std::vector
<vcl::filter::PDFElement
*>& rByteRangeElements
= pByteRange
->GetElements();
115 for (size_t i
= 0; i
< rByteRangeElements
.size(); ++i
)
117 auto pNumber
= dynamic_cast<vcl::filter::PDFNumberElement
*>(rByteRangeElements
[i
]);
120 SAL_WARN("xmlsecurity.pdfio",
121 "ValidateSignature: signature offset and length has to be a number");
127 nByteRangeOffset
= pNumber
->GetValue();
130 size_t nByteRangeLength
= pNumber
->GetValue();
131 aByteRanges
.emplace_back(nByteRangeOffset
, nByteRangeLength
);
134 // Detect if the byte ranges don't cover everything, but the signature itself.
135 if (aByteRanges
.size() < 2)
137 SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: expected 2 byte ranges");
140 if (aByteRanges
[0].first
!= 0)
142 SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: first range start is not 0");
145 // 2 is the leading "<" and the trailing ">" around the hex string.
146 size_t nSignatureLength
= static_cast<size_t>(pContents
->GetValue().getLength()) + 2;
147 if (aByteRanges
[1].first
!= (aByteRanges
[0].second
+ nSignatureLength
))
149 SAL_WARN("xmlsecurity.pdfio",
150 "ValidateSignature: second range start is not the end of the signature");
153 rStream
.Seek(STREAM_SEEK_TO_END
);
154 size_t nFileEnd
= rStream
.Tell();
155 if (bLast
&& (aByteRanges
[1].first
+ aByteRanges
[1].second
) != nFileEnd
)
156 // Second range end is not the end of the file.
157 rInformation
.bPartialDocumentSignature
= true;
159 // At this point there is no obviously missing info to validate the
161 std::vector
<unsigned char> aSignature
= vcl::filter::PDFDocument::DecodeHexString(pContents
);
162 if (aSignature
.empty())
164 SAL_WARN("xmlsecurity.pdfio", "ValidateSignature: empty contents");
168 return svl::crypto::Signing::Verify(rStream
, aByteRanges
, bNonDetached
, aSignature
,
173 } // namespace xmlsecurity
175 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */