diff --git a/PdfForQtLib/sources/pdfsignaturehandler.cpp b/PdfForQtLib/sources/pdfsignaturehandler.cpp index 8a3902a..9e274bb 100644 --- a/PdfForQtLib/sources/pdfsignaturehandler.cpp +++ b/PdfForQtLib/sources/pdfsignaturehandler.cpp @@ -19,6 +19,7 @@ #include "pdfdocument.h" #include "pdfencoding.h" #include "pdfform.h" +#include "pdfutils.h" #include "pdfsignaturehandler_impl.h" #include @@ -105,7 +106,7 @@ PDFSignature PDFSignature::parse(const PDFObjectStorage* storage, PDFObject obje result.m_byteRanges.reserve(byteRangeCount); for (size_t i = 0; i < byteRangeCount; ++i) { - ByteRange byteRange = { byteRangesArray[i], byteRangesArray[i + 1] }; + ByteRange byteRange = { byteRangesArray[2 * i], byteRangesArray[2 * i + 1] }; result.m_byteRanges.push_back(byteRange); } @@ -287,6 +288,18 @@ void PDFSignatureVerificationResult::addSignatureDataOtherError() m_errors << PDFTranslationContext::tr("Signed data are invalid."); } +void PDFSignatureVerificationResult::addSignatureDataCoveredBySignatureMissingError() +{ + m_flags.setFlag(Error_Signature_DataCoveredBySignatureMissing); + m_errors << PDFTranslationContext::tr("Data covered by signature are not present."); +} + +void PDFSignatureVerificationResult::addSignatureNotCoveredBytesWarning(PDFInteger count) +{ + m_flags.setFlag(Warning_Signature_NotCoveredBytes); + m_warnings << PDFTranslationContext::tr("%1 bytes are not covered by signature.").arg(count); +} + void PDFSignatureVerificationResult::setSignatureFieldQualifiedName(const QString& signatureFieldQualifiedName) { m_signatureFieldQualifiedName = signatureFieldQualifiedName; @@ -297,6 +310,14 @@ void PDFSignatureVerificationResult::setSignatureFieldReference(PDFObjectReferen m_signatureFieldReference = signatureFieldReference; } +void PDFSignatureVerificationResult::validate() +{ + if (isCertificateValid() && isSignatureValid()) + { + m_flags.setFlag(OK); + } +} + void PDFPublicKeySignatureHandler::initializeResult(PDFSignatureVerificationResult& result) const { PDFObjectReference signatureFieldReference = m_signatureField->getSelfReference(); @@ -457,6 +478,86 @@ void PDFPublicKeySignatureHandler::verifyCertificate(PDFSignatureVerificationRes } } +BIO* PDFPublicKeySignatureHandler::getSignedDataBuffer(pdf::PDFSignatureVerificationResult& result, QByteArray& outputBuffer) const +{ + const PDFSignature& signature = m_signatureField->getSignature(); + const QByteArray& contents = signature.getContents(); + const QByteArray& sourceData = m_sourceData; + + PDFInteger size = 0; + const PDFSignature::ByteRanges& byteRanges = signature.getByteRanges(); + for (const PDFSignature::ByteRange& byteRange : byteRanges) + { + size += byteRange.size; + } + + // Sanity checks + if (size > sourceData.size()) + { + result.addSignatureDataCoveredBySignatureMissingError(); + return nullptr; + } + + PDFClosedIntervalSet bytesCoveredBySignature; + + outputBuffer.reserve(size); + for (const PDFSignature::ByteRange& byteRange : byteRanges) + { + PDFInteger startOffset = byteRange.offset; // Offset to the first data byte + PDFInteger endOffset = byteRange.offset + byteRange.size; // Offset to the byte following last data byte + + if (startOffset == endOffset) + { + // This means byte range is zero + continue; + } + + if (startOffset > endOffset || startOffset < 0 || endOffset < 0 || startOffset >= m_sourceData.size() || endOffset > m_sourceData.size()) + { + result.addSignatureDataCoveredBySignatureMissingError(); + return nullptr; + } + + const int length = endOffset - startOffset; + outputBuffer.append(sourceData.constData() + startOffset, length); + bytesCoveredBySignature.addInterval(startOffset, endOffset - 1); + } + + // Jakub Melka: We must find byte string, which corresponds to signature. + // We find only first occurence, because second one should not exist - because + // it will mean that signature must be covered by itself. + QByteArray hexContents = contents.toHex(); + int index = sourceData.indexOf(hexContents); + if (index == -1) + { + index = sourceData.indexOf(hexContents.toUpper()); + } + if (index != -1) + { + int firstByteIndex = index; + int lastByteIndex = index + hexContents.size() - 1; + + if (firstByteIndex > 0 && sourceData[firstByteIndex - 1] == '<') + { + --firstByteIndex; + } + if (lastByteIndex + 1 < sourceData.size() && sourceData[lastByteIndex + 1] == '>') + { + ++lastByteIndex; + } + bytesCoveredBySignature.addInterval(firstByteIndex, lastByteIndex); + } + + // We add a warning, that this signature doesn't cover whole source byte range + if (!bytesCoveredBySignature.isCovered(0, sourceData.size() - 1)) + { + const PDFInteger notCoveredBytes = sourceData.size() - int(bytesCoveredBySignature.getTotalLength()); + result.addSignatureNotCoveredBytesWarning(notCoveredBytes); + } + + return BIO_new_mem_buf(outputBuffer.data(), outputBuffer.length()); +} + void PDFPublicKeySignatureHandler::verifySignature(PDFSignatureVerificationResult& result) const { PDFOpenSSLGlobalLock lock; @@ -471,7 +572,8 @@ void PDFPublicKeySignatureHandler::verifySignature(PDFSignatureVerificationResul const unsigned char* data = reinterpret_cast(content.data()); if (PKCS7* pkcs7 = d2i_PKCS7(nullptr, &data, content.size())) { - if (BIO* inputBuffer = getSignedDataBuffer(result)) + QByteArray buffer; + if (BIO* inputBuffer = getSignedDataBuffer(result, buffer)) { if (BIO* dataBio = PKCS7_dataInit(pkcs7, inputBuffer)) { @@ -534,6 +636,10 @@ void PDFPublicKeySignatureHandler::verifySignature(PDFSignatureVerificationResul BIO_free(inputBuffer); } + else + { + // There is no need for adding error, error is in this case added by getSignedDataBuffer function + } PKCS7_free(pkcs7); } @@ -554,6 +660,7 @@ PDFSignatureVerificationResult PDFSignatureHandler_adbe_pkcs7_detached::verify() initializeResult(result); verifyCertificate(result); verifySignature(result); + result.validate(); return result; } diff --git a/PdfForQtLib/sources/pdfsignaturehandler.h b/PdfForQtLib/sources/pdfsignaturehandler.h index 72c4545..34470bc 100644 --- a/PdfForQtLib/sources/pdfsignaturehandler.h +++ b/PdfForQtLib/sources/pdfsignaturehandler.h @@ -247,36 +247,39 @@ public: enum VerificationFlag { - None = 0x00000, ///< Used only for initialization - OK = 0x00001, ///< Both certificate and signature is OK - Certificate_OK = 0x00002, ///< Certificate is OK - Signature_OK = 0x00004, ///< Signature is OK - Error_NoHandler = 0x00008, ///< No signature handler for given signature - Error_Generic = 0x00010, ///< Generic error (uknown general error) + None = 0x00000000, ///< Used only for initialization + OK = 0x00000001, ///< Both certificate and signature is OK + Certificate_OK = 0x00000002, ///< Certificate is OK + Signature_OK = 0x00000004, ///< Signature is OK + Error_NoHandler = 0x00000008, ///< No signature handler for given signature + Error_Generic = 0x00000010, ///< Generic error (uknown general error) - Error_Certificate_Invalid = 0x00020, ///< Certificate is invalid - Error_Certificate_NoSignatures = 0x00040, ///< No signature found in certificate data - Error_Certificate_Missing = 0x00080, ///< Certificate is missing - Error_Certificate_Generic = 0x00100, ///< Generic error during certificate verification - Error_Certificate_Expired = 0x00200, ///< Certificate has expired - Error_Certificate_SelfSigned = 0x00400, ///< Self signed certificate - Error_Certificate_SelfSignedChain = 0x00800, ///< Self signed certificate in chain - Error_Certificate_TrustedNotFound = 0x01000, ///< No trusted certificate was found - Error_Certificate_Revoked = 0x02000, ///< Certificate has been revoked - Error_Certificate_Other = 0x04000, ///< Other certificate error. See OpenSSL code for details. + Error_Certificate_Invalid = 0x00000020, ///< Certificate is invalid + Error_Certificate_NoSignatures = 0x00000040, ///< No signature found in certificate data + Error_Certificate_Missing = 0x00000080, ///< Certificate is missing + Error_Certificate_Generic = 0x00000100, ///< Generic error during certificate verification + Error_Certificate_Expired = 0x00000200, ///< Certificate has expired + Error_Certificate_SelfSigned = 0x00000400, ///< Self signed certificate + Error_Certificate_SelfSignedChain = 0x00000800, ///< Self signed certificate in chain + Error_Certificate_TrustedNotFound = 0x00001000, ///< No trusted certificate was found + Error_Certificate_Revoked = 0x00002000, ///< Certificate has been revoked + Error_Certificate_Other = 0x00004000, ///< Other certificate error. See OpenSSL code for details. - Error_Signature_Invalid = 0x08000, ///< Signature is invalid for some reason - Error_Signature_SourceCertificateMissing = 0x10000, ///< Source certificate of signature is missing - Error_Signature_NoSignaturesFound = 0x20000, ///< No signatures found - Error_Signature_DigestFailure = 0x40000, ///< Digest failure - Error_Signature_DataOther = 0x80000, ///< Signed data were not verified + Error_Signature_Invalid = 0x00008000, ///< Signature is invalid for some reason + Error_Signature_SourceCertificateMissing = 0x00010000, ///< Source certificate of signature is missing + Error_Signature_NoSignaturesFound = 0x00020000, ///< No signatures found + Error_Signature_DigestFailure = 0x00040000, ///< Digest failure + Error_Signature_DataOther = 0x00080000, ///< Signed data were not verified + Error_Signature_DataCoveredBySignatureMissing = 0x00100000, ///< Data covered by signature are not present + + Warning_Signature_NotCoveredBytes = 0x00200000, ///< Some bytes in source data are not covered by signature Error_Certificates_Mask = Error_Certificate_Invalid | Error_Certificate_NoSignatures | Error_Certificate_Missing | Error_Certificate_Generic | Error_Certificate_Expired | Error_Certificate_SelfSigned | Error_Certificate_SelfSignedChain | Error_Certificate_TrustedNotFound | Error_Certificate_Revoked | Error_Certificate_Other, Error_Signatures_Mask = Error_Signature_Invalid | Error_Signature_SourceCertificateMissing | Error_Signature_NoSignaturesFound | - Error_Signature_DigestFailure | Error_Signature_DataOther, + Error_Signature_DigestFailure | Error_Signature_DataOther | Error_Signature_DataCoveredBySignatureMissing, }; Q_DECLARE_FLAGS(VerificationFlags, VerificationFlag) @@ -299,6 +302,8 @@ public: void addSignatureCertificateMissingError(); void addSignatureDigestFailureError(); void addSignatureDataOtherError(); + void addSignatureDataCoveredBySignatureMissingError(); + void addSignatureNotCoveredBytesWarning(PDFInteger count); bool isValid() const { return hasFlag(OK); } bool isCertificateValid() const { return hasFlag(Certificate_OK); } @@ -312,17 +317,22 @@ public: PDFObjectReference getSignatureFieldReference() const { return m_signatureFieldReference; } const QString& getSignatureFieldQualifiedName() const { return m_signatureFieldQualifiedName; } const QStringList& getErrors() const { return m_errors; } + const QStringList& getWarnings() const { return m_warnings; } void setSignatureFieldQualifiedName(const QString& signatureFieldQualifiedName); void setSignatureFieldReference(PDFObjectReference signatureFieldReference); void addCertificateInfo(PDFCertificateInfo info) { m_certificateInfos.emplace_back(qMove(info)); } + /// Adds OK flag, if both certificate and signature are valid + void validate(); + private: VerificationFlags m_flags = None; PDFObjectReference m_signatureFieldReference; QString m_signatureFieldQualifiedName; QStringList m_errors; + QStringList m_warnings; PDFCertificateInfos m_certificateInfos; }; diff --git a/PdfForQtLib/sources/pdfsignaturehandler_impl.h b/PdfForQtLib/sources/pdfsignaturehandler_impl.h index d337be9..2aef521 100644 --- a/PdfForQtLib/sources/pdfsignaturehandler_impl.h +++ b/PdfForQtLib/sources/pdfsignaturehandler_impl.h @@ -43,6 +43,8 @@ protected: void verifySignature(PDFSignatureVerificationResult& result) const; void addTrustedCertificates(X509_STORE* store) const; + BIO* getSignedDataBuffer(PDFSignatureVerificationResult& result, QByteArray& outputBuffer) const; + /// Return a list of certificates from PKCS7 object static STACK_OF(X509)* getCertificates(PKCS7* pkcs7); diff --git a/PdfForQtLib/sources/pdfutils.cpp b/PdfForQtLib/sources/pdfutils.cpp index 8a73f45..5dd07c1 100644 --- a/PdfForQtLib/sources/pdfutils.cpp +++ b/PdfForQtLib/sources/pdfutils.cpp @@ -278,4 +278,75 @@ std::vector PDFDependentLibraryInfo::getLibraryInfo() return result; } +void PDFClosedIntervalSet::addInterval(PDFInteger low, PDFInteger high) +{ + m_intervals.emplace_back(low, high); + normalize(); +} + +bool PDFClosedIntervalSet::isCovered(PDFInteger low, PDFInteger high) +{ + PDFClosedIntervalSet temporary; + temporary.addInterval(low, high); + return *this == temporary; +} + +PDFInteger PDFClosedIntervalSet::getTotalLength() const +{ + return std::accumulate(m_intervals.cbegin(), m_intervals.cend(), 0, [](PDFInteger count, const auto& b) { return count + b.second - b.first + 1; }); +} + +void PDFClosedIntervalSet::normalize() +{ + // Algorithm: + // 1) sort all ranges + // 2) merge adjacent ones + + qSort(m_intervals); + + std::vector intervals; + auto it = m_intervals.cbegin(); + auto itEnd = m_intervals.cend(); + + while (it != itEnd) + { + ClosedInterval interval = *it++; + + while (it != itEnd && overlapsOrAdjacent(interval, *it)) + { + interval = std::make_pair(qMin(interval.first, it->first), qMax(interval.second, it->second)); + ++it; + } + + intervals.push_back(interval); + } + + m_intervals = qMove(intervals); +} + +bool PDFClosedIntervalSet::overlapsOrAdjacent(ClosedInterval a, ClosedInterval b) +{ + if (a.first > b.first) + { + std::swap(a, b); + } + + // There are 3 cases + // a b + // |---| |---| 1) + // + // a b + // |---||---| 2) + // + // a b + // |---| 3) + // |---| + // + // We cover cases 2) and 3) with following condition: + // [a1, a2], [b1, b2] + // b1 <= a2 + 1, because we can have intervals [1,2], [3,4] - these should be merged + + return b.first <= a.second + 1; +} + } // namespace pdf diff --git a/PdfForQtLib/sources/pdfutils.h b/PdfForQtLib/sources/pdfutils.h index 1c7296b..a9d0c01 100644 --- a/PdfForQtLib/sources/pdfutils.h +++ b/PdfForQtLib/sources/pdfutils.h @@ -526,6 +526,47 @@ private: QString m_errorMessage; }; +/// Set of closed intervals +class PDFClosedIntervalSet +{ +public: + explicit inline PDFClosedIntervalSet() = default; + + bool operator ==(const PDFClosedIntervalSet&) const = default; + + using ClosedInterval = std::pair; + + /// Adds closed interval, where \p low is lower bound + /// of the closed interval, and high is upper bound + /// of closed interval. + /// \param low Lower bound of interval + /// \param high Upper bound of interval + void addInterval(PDFInteger low, PDFInteger high); + + /// Adds a single value to the interval (closed interval + /// of single value) + /// \param value Value + void addValue(PDFInteger value) { addInterval(value, value); } + + /// Returns true, if given closed range is subset of + /// this interval set. + bool isCovered(PDFInteger low, PDFInteger high); + + /// Returns sum of interval lengths + PDFInteger getTotalLength() const; + +private: + /// Normalizes interval ranges - merges adjacent intervals + void normalize(); + + /// Returns true, if interval overlaps, or is adjacent to the other one + /// \param a First interval + /// \param b Second interval + static bool overlapsOrAdjacent(ClosedInterval a, ClosedInterval b); + + std::vector m_intervals; +}; + } // namespace pdf #endif // PDFUTILS_H