Signature validation

This commit is contained in:
Jakub Melka 2020-06-20 14:10:46 +02:00
parent f6c5431770
commit 3e6f4ada3d
5 changed files with 255 additions and 24 deletions

View File

@ -19,6 +19,7 @@
#include "pdfdocument.h"
#include "pdfencoding.h"
#include "pdfform.h"
#include "pdfutils.h"
#include "pdfsignaturehandler_impl.h"
#include <openssl/err.h>
@ -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<const unsigned char*>(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;
}

View File

@ -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;
};

View File

@ -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);

View File

@ -278,4 +278,75 @@ std::vector<PDFDependentLibraryInfo> 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<ClosedInterval> 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

View File

@ -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<PDFInteger, PDFInteger>;
/// 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<ClosedInterval> m_intervals;
};
} // namespace pdf
#endif // PDFUTILS_H