// Copyright (C) 2020 Jakub Melka // // This file is part of Pdf4Qt. // // Pdf4Qt is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Pdf4Qt is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with Pdf4Qt. If not, see . #ifndef PDFSIGNATUREHANDLER_H #define PDFSIGNATUREHANDLER_H #include "pdfglobal.h" #include "pdfobject.h" #include "pdfutils.h" #include #include #include class QDataStream; namespace pdf { class PDFForm; class PDFObjectStorage; class PDFCertificateStore; class PDFFormFieldSignature; class PDFDocumentSecurityStore; /// Signature reference dictionary. class PDFSignatureReference { public: enum class TransformMethod { Invalid, DocMDP, UR, FieldMDP }; TransformMethod getTransformMethod() const { return m_transformMethod; } const PDFObject& getTransformParams() const { return m_transformParams; } const PDFObject& getData() const { return m_data; } const QByteArray& getDigestMethod() const { return m_digestMethod; } /// Tries to parse the signature reference. No exception is thrown, in case of error, /// invalid signature reference object is returned. /// \param storage Object storage /// \param object Object containing the signature static PDFSignatureReference parse(const PDFObjectStorage* storage, PDFObject object); private: TransformMethod m_transformMethod = TransformMethod::Invalid; PDFObject m_transformParams; PDFObject m_data; QByteArray m_digestMethod; }; /// Signature dictionary. Contains digital signature. This signature can be validated by signature validator. /// This object contains certificates, digital signatures, and other information about signature. class PDFSignature { public: enum class Type { Invalid, Sig, DocTimeStamp }; struct ByteRange { PDFInteger offset = 0; PDFInteger size = 0; }; using ByteRanges = std::vector; struct Changes { PDFInteger pagesAltered = 0; PDFInteger fieldsAltered = 0; PDFInteger fieldsFilled = 0; }; enum class AuthentificationType { Invalid, PIN, Password, Fingerprint }; /// Tries to parse the signature. No exception is thrown, in case of error, /// invalid signature object is returned. /// \param storage Object storage /// \param object Object containing the signature static PDFSignature parse(const PDFObjectStorage* storage, PDFObject object); Type getType() const { return m_type; } const QByteArray& getFilter() const { return m_filter; } const QByteArray& getSubfilter() const { return m_subfilter; } const QByteArray& getContents() const { return m_contents; } const std::vector* getCertificates() const { return m_certificates.has_value() ? &m_certificates.value() : nullptr; } const ByteRanges& getByteRanges() const { return m_byteRanges; } const std::vector& getReferences() const { return m_references; } const Changes* getChanges() const { return m_changes.has_value() ? &m_changes.value() : nullptr; } const QString& getName() const { return m_name; } const QDateTime& getSigningDateTime() const { return m_signingDateTime; } const QString& getLocation() const { return m_location; } const QString& getReason() const { return m_reason; } const QString& getContactInfo() const { return m_contactInfo; } PDFInteger getR() const { return m_R; } PDFInteger getV() const { return m_V; } const PDFObject& getPropBuild() const { return m_propBuild; } PDFInteger getPropTime() const { return m_propTime; } AuthentificationType getAuthentificationType() const { return m_propType; } private: Type m_type = Type::Invalid; QByteArray m_filter; ///< Preferred signature handler name QByteArray m_subfilter; ///< Describes encoding of signature QByteArray m_contents; std::optional> m_certificates; ///< Certificate chain (only for adbe.x509.rsa_sha1) ByteRanges m_byteRanges; std::vector m_references; std::optional m_changes; QString m_name; ///< Name of signer. Should rather be extracted from signature. QDateTime m_signingDateTime; ///< Signing date and time. Should be extracted from signature, if possible. QString m_location; ///< CPU hostname or physical location of signing QString m_reason; ///< Reason for signing QString m_contactInfo; ///< Contact info for verifying the signature PDFInteger m_R; ///< Version of signature handler. Obsolete. PDFInteger m_V; ///< Version of signature dictionary format. 1 if References should be used. PDFObject m_propBuild; PDFInteger m_propTime = 0; AuthentificationType m_propType = AuthentificationType::Invalid; }; /// Info about certificate, various details etc. class Pdf4QtLIBSHARED_EXPORT PDFCertificateInfo { public: explicit inline PDFCertificateInfo() = default; void serialize(QDataStream& stream) const; void deserialize(QDataStream& stream); friend inline QDataStream& operator<<(QDataStream& stream, const PDFCertificateInfo& info) { info.serialize(stream); return stream; } friend inline QDataStream& operator>>(QDataStream& stream, PDFCertificateInfo& info) { info.deserialize(stream); return stream; } bool operator ==(const PDFCertificateInfo&) const = default; bool operator !=(const PDFCertificateInfo&) const = default; /// These entries are taken from RFC 5280, section 4.1.2.4, /// they are supported and loaded from the certificate, with /// exception of Email entry. enum NameEntry { CountryName, OrganizationName, OrganizationalUnitName, DistinguishedName, StateOrProvinceName, CommonName, SerialNumber, LocalityName, Title, Surname, GivenName, Initials, Pseudonym, GenerationalQualifier, Email, NameEnd }; enum PublicKey { KeyRSA, KeyDSA, KeyEC, KeyDH, KeyUnknown }; // This enum is defined in RFC 5280, chapter 4.2.1.3, Key Usage. Second part, // are defined in extended key usage entry, if it is defined. enum KeyUsageFlag : uint32_t { KeyUsageNone = 0x00000000, KeyUsageDigitalSignature = 0x00000080, KeyUsageNonRepudiation = 0x00000040, KeyUsageKeyEncipherment = 0x00000020, KeyUsageDataEncipherment = 0x00000010, KeyUsageAgreement = 0x00000008, KeyUsageCertSign = 0x00000004, KeyUsageCrlSign = 0x00000002, KeyUsageEncipherOnly = 0x00000001, KeyUsageDecipherOnly = 0x00008000, KeyUsageExtended_SSL_SERVER = 0x1 << 16, KeyUsageExtended_SSL_CLIENT = 0x2 << 16, KeyUsageExtended_SMIME = 0x4 << 16, KeyUsageExtended_CODE_SIGN = 0x8 << 16, KeyUsageExtended_SGC = 0x10 << 16, KeyUsageExtended_OCSP_SIGN = 0x20 << 16, KeyUsageExtended_TIMESTAMP = 0x40 << 16, KeyUsageExtended_DVCS = 0x80 << 16, KeyUsageExtended_ANYEKU = 0x100 << 16, }; Q_DECLARE_FLAGS(KeyUsageFlags, KeyUsageFlag) const QString& getName(NameEntry name) const { return m_nameEntries[name]; } void setName(NameEntry name, QString string) { m_nameEntries[name] = qMove(string); } QDateTime getNotValidBefore() const; void setNotValidBefore(const QDateTime& notValidBefore); QDateTime getNotValidAfter() const; void setNotValidAfter(const QDateTime& notValidAfter); int32_t getVersion() const; void setVersion(int32_t version); PublicKey getPublicKey() const; void setPublicKey(const PublicKey& publicKey); int getKeySize() const; void setKeySize(int keySize); KeyUsageFlags getKeyUsage() const; void setKeyUsage(KeyUsageFlags keyUsage); QByteArray getCertificateData() const; void setCertificateData(const QByteArray& certificateData); /// Creates certificate info from binary data. Binary data must /// contain DER-encoded certificate. /// \param certificateData Data static std::optional getCertificateInfo(const QByteArray& certificateData); private: static constexpr int persist_version = 1; int32_t m_version = 0; int m_keySize = 0; PublicKey m_publicKey = KeyUnknown; std::array m_nameEntries; QDateTime m_notValidBefore; QDateTime m_notValidAfter; KeyUsageFlags m_keyUsage; QByteArray m_certificateData; }; using PDFCertificateInfos = std::vector; class Pdf4QtLIBSHARED_EXPORT PDFSignatureVerificationResult { public: explicit PDFSignatureVerificationResult() = default; explicit PDFSignatureVerificationResult(PDFSignature::Type type, PDFObjectReference signatureFieldReference, QString qualifiedName) : m_type(type), m_signatureFieldReference(signatureFieldReference), m_signatureFieldQualifiedName(qualifiedName) { } enum class Status { OK, Warning, Error }; enum VerificationFlag { 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 = 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 = 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 Warning_Certificate_CRLValidityTimeExpired = 0x00400000, ///< Certificate revocation list was not checked, because it's validity expired Warning_Certificate_QualifiedStatement = 0x00800000, ///< Qualified certificate statement not verified 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_DataCoveredBySignatureMissing, Warning_Certificates_Mask = Warning_Certificate_CRLValidityTimeExpired | Warning_Certificate_QualifiedStatement, Warning_Signatures_Mask = Warning_Signature_NotCoveredBytes, Warnings_Mask = Warning_Certificates_Mask | Warning_Signatures_Mask }; Q_DECLARE_FLAGS(VerificationFlags, VerificationFlag) PDFSignature::Type getType() const; void setType(const PDFSignature::Type& type); /// Adds no handler error for given signature format /// \param format Signature format void addNoHandlerError(const QByteArray& format); void addInvalidCertificateError(); void addNoSignaturesError(); void addCertificateMissingError(); void addCertificateGenericError(); void addCertificateExpiredError(); void addCertificateSelfSignedError(); void addCertificateSelfSignedInChainError(); void addCertificateTrustedNotFoundError(); void addCertificateRevokedError(); void addCertificateOtherError(int error); void addInvalidSignatureError(); void addSignatureNoSignaturesFoundError(); void addSignatureCertificateMissingError(); void addSignatureDigestFailureError(); void addSignatureDataOtherError(); void addSignatureDataCoveredBySignatureMissingError(); void addSignatureNotCoveredBytesWarning(PDFInteger count); void addCertificateCRLValidityTimeExpiredWarning(); void addCertificateQualifiedStatementNotVerifiedWarning(); bool isValid() const { return hasFlag(OK); } bool isCertificateValid() const { return hasFlag(Certificate_OK); } bool isSignatureValid() const { return hasFlag(Signature_OK); } bool hasError() const { return !isValid(); } bool hasWarning() const { return m_flags & Warnings_Mask; } bool hasCertificateError() const { return m_flags & Error_Certificates_Mask; } bool hasSignatureError() const { return m_flags & Error_Signatures_Mask; } bool hasCertificateWarning() const { return m_flags & Warning_Certificates_Mask; } bool hasSignatureWarning() const { return m_flags & Warning_Signatures_Mask; } bool hasFlag(VerificationFlag flag) const { return m_flags.testFlag(flag); } void setFlag(VerificationFlag flag, bool value) { m_flags.setFlag(flag, value); } 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; } const QStringList& getHashAlgorithms() const { return m_hashAlgorithms; } const PDFCertificateInfos& getCertificateInfos() const { return m_certificateInfos; } void setSignatureFieldQualifiedName(const QString& signatureFieldQualifiedName); void setSignatureFieldReference(PDFObjectReference signatureFieldReference); void addCertificateInfo(PDFCertificateInfo info) { m_certificateInfos.emplace_back(qMove(info)); } void addHashAlgorithm(const QString& algorithm); /// Adds OK flag, if both certificate and signature are valid void validate(); QDateTime getSignatureDate() const; void setSignatureDate(const QDateTime& signatureDate); QDateTime getTimestampDate() const; void setTimestampDate(const QDateTime& timestampDate); QByteArray getSignatureHandler() const; void setSignatureHandler(const QByteArray& signatureFilter); Status getCertificateStatus() const; Status getSignatureStatus() const; QString getCertificateStatusText() const { return getStatusText(getCertificateStatus()); } QString getSignatureStatusText() const { return getStatusText(getSignatureStatus()); } static QString getStatusText(Status status); const PDFClosedIntervalSet& getBytesCoveredBySignature() const; void setBytesCoveredBySignature(const PDFClosedIntervalSet& bytesCoveredBySignature); private: PDFSignature::Type m_type = PDFSignature::Type::Invalid; VerificationFlags m_flags = None; PDFObjectReference m_signatureFieldReference; QString m_signatureFieldQualifiedName; QDateTime m_signatureDate; QDateTime m_timestampDate; QStringList m_errors; QStringList m_warnings; QStringList m_hashAlgorithms; QByteArray m_signatureHandler; PDFCertificateInfos m_certificateInfos; PDFClosedIntervalSet m_bytesCoveredBySignature; }; /// Signature handler. Can verify both certificate and signature validity. class Pdf4QtLIBSHARED_EXPORT PDFSignatureHandler { public: explicit PDFSignatureHandler() = default; virtual ~PDFSignatureHandler() = default; virtual PDFSignatureVerificationResult verify() const = 0; struct Parameters { const PDFCertificateStore* store = nullptr; const PDFDocumentSecurityStore* dss = nullptr; bool enableVerification = true; bool ignoreExpirationDate = false; bool useSystemCertificateStore = true; }; /// Tries to verify all signatures in the form. If form is invalid, then /// empty vector is returned. /// \param form Form /// \param sourceData Source data /// \param parameters Verification settings static std::vector verifySignatures(const PDFForm& form, const QByteArray& sourceData, const Parameters& parameters); private: /// Creates signature handler using format specified by signature in signature field. /// If signature format is unknown, then nullptr is returned. /// \param signatureField Signature field /// \param sourceData /// \param parameters Verification settings static PDFSignatureHandler* createHandler(const PDFFormFieldSignature* signatureField, const QByteArray& sourceData, const Parameters& parameters); }; /// Trusted certificate store. Contains list of trusted certificates. Store /// can be persisted to the persistent storage trough serialization/deserialization. /// Persisting method is versioned. class Pdf4QtLIBSHARED_EXPORT PDFCertificateStore { public: explicit inline PDFCertificateStore() = default; void serialize(QDataStream& stream) const; void deserialize(QDataStream& stream); enum class EntryType : int { User, ///< Certificate has been added manually by the user EUTL, ///< Certificate comes EU trusted list System, ///< System certificate }; struct CertificateEntry { void serialize(QDataStream& stream) const; void deserialize(QDataStream& stream); friend inline QDataStream& operator<<(QDataStream& stream, const CertificateEntry& entry) { entry.serialize(stream); return stream; } friend inline QDataStream& operator>>(QDataStream& stream, CertificateEntry& entry) { entry.deserialize(stream); return stream; } static constexpr int persist_version = 1; EntryType type = EntryType::User; PDFCertificateInfo info; }; using CertificateEntries = std::vector; /// Tries to add new certificate to the certificate store. If certificate /// is already here, then nothing happens and function returns true. /// Otherwise data are checked, if they really contains a certificate, /// and if yes, then it is added. Function returns false if, and only if, /// error occured and certificate was not added. /// \param type Type /// \param certificate Certificate bool add(EntryType type, const QByteArray& certificate); /// Tries to add new certificate to the certificate store. If certificate /// is already here, then nothing happens and function returns true. /// Otherwise data are checked, if they really contains a certificate, /// and if yes, then it is added. Function returns false if, and only if, /// error occured and certificate was not added. /// \param type Type /// \param info Certificate info bool add(EntryType type, PDFCertificateInfo info); /// Returns true, if storage contains given certificate /// \param info Certificate info bool contains(const PDFCertificateInfo& info); /// Get certificates stored in the store const CertificateEntries& getCertificates() const { return m_certificates; } /// Set certificates void setCertificates(CertificateEntries certificates) { m_certificates = qMove(certificates); } /// Returns default certificate store file name QString getDefaultCertificateStoreFileName() const; /// Load from default user certificate storage void loadDefaultUserCertificates(); /// Save to default user certificate storage void saveDefaultUserCertificates(); /// Creates default directory for certificate store void createDirectoryForDefaultUserCertificatesStore(); /// Returns a list of system certificates static CertificateEntries getSystemCertificates(); private: static constexpr int persist_version = 1; CertificateEntries m_certificates; }; } // namespace pdf #endif // PDFSIGNATUREHANDLER_H