Commit a234a902 authored by Chinmoy Ranjan Pradhan's avatar Chinmoy Ranjan Pradhan Committed by Albert Astals Cid

Implement digital signature support for PDF

This is a squash of the original branch

It also contains fixes/rework from Albert Astals Cid

If you're interested in its history have a look at the
gsoc2018_digitalsignature branch

The poppler dependency situation is:
 * With 0.51 you get most signature information
 * With 0.68 you also get signature location/reason information
 * With 0.73 you also get signature certificate information
parent ab96e0c0
......@@ -195,6 +195,7 @@ set(okularcore_SRCS
core/utils.cpp
core/view.cpp
core/fileprinter.cpp
core/signatureutils.cpp
core/script/event.cpp
core/synctex/synctex_parser.c
core/synctex/synctex_parser_utils.c
......@@ -219,6 +220,7 @@ install( FILES
core/page.h
core/pagesize.h
core/pagetransition.h
core/signatureutils.h
core/sound.h
core/sourcereference.h
core/textdocumentgenerator.h
......@@ -350,6 +352,7 @@ set(okularpart_SRCS
ui/annotationtools.cpp
ui/annotationwidgets.cpp
ui/bookmarklist.cpp
ui/certificateviewer.cpp
ui/debug_ui.cpp
ui/drawingtoolactions.cpp
ui/fileprinterpreview.cpp
......@@ -371,6 +374,7 @@ set(okularpart_SRCS
ui/presentationsearchbar.cpp
ui/presentationwidget.cpp
ui/propertiesdialog.cpp
ui/revisionviewer.cpp
ui/searchlineedit.cpp
ui/searchwidget.cpp
ui/sidebar.cpp
......@@ -382,6 +386,10 @@ set(okularpart_SRCS
ui/toolaction.cpp
ui/videowidget.cpp
ui/layers.cpp
ui/signatureguiutils.cpp
ui/signaturepropertiesdialog.cpp
ui/signaturemodel.cpp
ui/signaturepanel.cpp
)
if (Qt5TextToSpeech_FOUND)
......
......@@ -82,3 +82,8 @@ ecm_add_test(generatorstest.cpp
LINK_LIBRARIES Qt5::Test KF5::CoreAddons okularcore
)
target_compile_definitions(generatorstest PRIVATE GENERATORS_BUILD_DIR="${CMAKE_BINARY_DIR}/generators")
ecm_add_test(signatureformtest.cpp
TEST_NAME "signatureformtest"
LINK_LIBRARIES Qt5::Test okularcore
)
/***************************************************************************
* Copyright (C) 2018 by Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include <QtTest>
#include <QMimeType>
#include <QMimeDatabase>
#include "../settings_core.h"
#include <core/document.h>
#include <core/form.h>
#include <core/page.h>
class SignatureFormTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void testSignatureForm();
private:
Okular::Document *m_document;
};
void SignatureFormTest::initTestCase()
{
Okular::SettingsCore::instance( QStringLiteral("signatureformtest") );
m_document = new Okular::Document( nullptr );
}
void SignatureFormTest::cleanupTestCase()
{
delete m_document;
}
void SignatureFormTest::testSignatureForm()
{
#ifndef HAVE_POPPLER_0_73
const QString testFile = QStringLiteral(KDESRCDIR "data/pdf_with_signature.pdf");
QMimeDatabase db;
const QMimeType mime = db.mimeTypeForFile( testFile );
QCOMPARE( m_document->openDocument(testFile, QUrl(), mime), Okular::Document::OpenSuccess );
const Okular::Page *page = m_document->page( 0 );
QLinkedList< Okular::FormField * > pageFields = page->formFields();
QCOMPARE( pageFields.size(), 1 );
QCOMPARE( pageFields.first()->type(), Okular::FormField::FormSignature );
Okular::FormFieldSignature *sf = static_cast< Okular::FormFieldSignature * >( pageFields.first() );
QCOMPARE( sf->signatureType(), Okular::FormFieldSignature::AdbePkcs7detached );
#endif
}
QTEST_MAIN( SignatureFormTest )
#include "signatureformtest.moc"
......@@ -5093,6 +5093,25 @@ QAbstractItemModel * Document::layersModel() const
return d->m_generator ? d->m_generator->layersModel() : nullptr;
}
QByteArray Document::requestSignedRevisionData( const Okular::SignatureInfo &info )
{
QFile f( d->m_docFileName );
if ( !f.open( QIODevice::ReadOnly ) )
{
KMessageBox::error( nullptr, i18n("Could not open '%1'. File does not exist", d->m_docFileName ) );
return {};
}
const QList<qint64> byteRange = info.signedRangeBounds();
f.seek( byteRange.first() );
QByteArray data;
QDataStream stream( &data, QIODevice::WriteOnly );
stream << f.read( byteRange.last() - byteRange.first() );
f.close();
return data;
}
void DocumentPrivate::requestDone( PixmapRequest * req )
{
if ( !req )
......
......@@ -61,6 +61,7 @@ class RenditionAction;
class SourceReference;
class View;
class VisiblePageRect;
class SignatureInfo;
/** IDs for seaches. Globally defined here. **/
#define PART_SEARCH_ID 1
......@@ -1026,6 +1027,13 @@ class OKULARCORE_EXPORT Document : public QObject
*/
void reloadDocument() const;
/**
* Returns the part of document covered by the given signature @p info.
*
* @since 1.7
*/
QByteArray requestSignedRevisionData( const Okular::SignatureInfo &info );
Q_SIGNALS:
/**
* This signal is emitted whenever the document is about to close.
......
......@@ -294,3 +294,32 @@ bool FormFieldChoice::canBeSpellChecked() const
return false;
}
class Okular::FormFieldSignaturePrivate : public Okular::FormFieldPrivate
{
public:
FormFieldSignaturePrivate()
: FormFieldPrivate( FormField::FormSignature )
{
}
Q_DECLARE_PUBLIC( FormFieldSignature )
void setValue( const QString& v ) override
{
Q_UNUSED( v )
}
QString value() const override
{
return QString();
}
};
FormFieldSignature::FormFieldSignature()
: FormField( *new FormFieldSignaturePrivate() )
{
}
FormFieldSignature::~FormFieldSignature()
{
}
......@@ -13,9 +13,12 @@
#include "okularcore_export.h"
#include "area.h"
#include "annotations.h"
#include "signatureutils.h"
#include <QStringList>
#include <memory>
namespace Okular {
class Action;
......@@ -25,6 +28,7 @@ class FormFieldPrivate;
class FormFieldButtonPrivate;
class FormFieldTextPrivate;
class FormFieldChoicePrivate;
class FormFieldSignaturePrivate;
/**
* @short The base interface of a form field.
......@@ -387,6 +391,46 @@ class OKULARCORE_EXPORT FormFieldChoice : public FormField
Q_DISABLE_COPY( FormFieldChoice )
};
/**
* @short Interface of a signature form field.
*
* This is the base interface to reimplement to represent a signature field.
*/
class OKULARCORE_EXPORT FormFieldSignature : public FormField
{
public:
/**
* The types of signature.
*/
enum SignatureType {
AdbePkcs7sha1,
AdbePkcs7detached,
EtsiCAdESdetached,
UnknownType
};
~FormFieldSignature();
/**
* The signature type
*/
virtual SignatureType signatureType() const = 0;
/**
* The signature info
*/
virtual const SignatureInfo &signatureInfo() const = 0;
protected:
FormFieldSignature();
private:
Q_DECLARE_PRIVATE( FormFieldSignature )
Q_DISABLE_COPY( FormFieldSignature )
};
}
Q_DECLARE_METATYPE(const Okular::FormFieldSignature *);
#endif
/***************************************************************************
* Copyright (C) 2018 by Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include "signatureutils.h"
using namespace Okular;
CertificateInfo::CertificateInfo()
{
}
CertificateInfo::~CertificateInfo()
{
}
Q_DECLARE_OPERATORS_FOR_FLAGS( CertificateInfo::KeyUsageExtensions )
bool CertificateInfo::isNull() const
{
return true;
}
int CertificateInfo::version() const
{
return -1;
}
QByteArray CertificateInfo::serialNumber() const
{
return QByteArray();
}
QString CertificateInfo::issuerInfo(EntityInfoKey) const
{
return QString();
}
QString CertificateInfo::subjectInfo(EntityInfoKey) const
{
return QString();
}
QDateTime CertificateInfo::validityStart() const
{
return QDateTime();
}
QDateTime CertificateInfo::validityEnd() const
{
return QDateTime();
}
CertificateInfo::KeyUsageExtensions CertificateInfo::keyUsageExtensions() const
{
return KuNone;
}
QByteArray CertificateInfo::publicKey() const
{
return QByteArray();
}
CertificateInfo::PublicKeyType CertificateInfo::publicKeyType() const
{
return OtherKey;
}
int CertificateInfo::publicKeyStrength() const
{
return -1;
}
bool CertificateInfo::isSelfSigned() const
{
return false;
}
QByteArray CertificateInfo::certificateData() const
{
return QByteArray();
}
SignatureInfo::SignatureInfo()
{
}
SignatureInfo::~SignatureInfo()
{
}
SignatureInfo::SignatureStatus SignatureInfo::signatureStatus() const
{
return SignatureStatusUnknown;
}
SignatureInfo::CertificateStatus SignatureInfo::certificateStatus() const
{
return CertificateStatusUnknown;
}
SignatureInfo::HashAlgorithm SignatureInfo::hashAlgorithm() const
{
return HashAlgorithmUnknown;
}
QString SignatureInfo::signerName() const
{
return QString();
}
QString SignatureInfo::signerSubjectDN() const
{
return QString();
}
QString SignatureInfo::location() const
{
return QString();
}
QString SignatureInfo::reason() const
{
return QString();
}
QDateTime SignatureInfo::signingTime() const
{
return QDateTime();
}
QByteArray SignatureInfo::signature() const
{
return QByteArray();
}
QList<qint64> SignatureInfo::signedRangeBounds() const
{
return QList<qint64>();
}
bool SignatureInfo::signsTotalDocument() const
{
return false;
}
const CertificateInfo &SignatureInfo::certificateInfo() const
{
static CertificateInfo dummy;
return dummy;
}
/***************************************************************************
* Copyright (C) 2018 by Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#ifndef OKULAR_SIGNATUREINFO_H
#define OKULAR_SIGNATUREINFO_H
#include "okularcore_export.h"
#include <QFlag>
#include <QList>
#include <QString>
#include <QDateTime>
#include <QSharedPointer>
namespace Okular {
class CertificateInfoPrivate;
class SignatureInfoPrivate;
/**
* @short A helper class to store information about x509 certificate
*/
class OKULARCORE_EXPORT CertificateInfo
{
public:
/**
* The algorithm of public key.
*/
enum PublicKeyType
{
RsaKey,
DsaKey,
EcKey,
OtherKey
};
/**
* Certificate key usage extensions.
*/
enum KeyUsageExtension
{
KuDigitalSignature = 0x80,
KuNonRepudiation = 0x40,
KuKeyEncipherment = 0x20,
KuDataEncipherment = 0x10,
KuKeyAgreement = 0x08,
KuKeyCertSign = 0x04,
KuClrSign = 0x02,
KuEncipherOnly = 0x01,
KuNone = 0x00
};
Q_DECLARE_FLAGS( KeyUsageExtensions, KeyUsageExtension )
/**
* Predefined keys for elements in an entity's distinguished name.
*/
enum EntityInfoKey
{
CommonName,
DistinguishedName,
EmailAddress,
Organization,
};
/**
* Destructor
*/
virtual ~CertificateInfo();
/**
* Returns true if certificate has no contents; otherwise returns false.
*/
virtual bool isNull() const;
/**
* The certificate version string.
*/
virtual int version() const;
/**
* The certificate serial number.
*/
virtual QByteArray serialNumber() const;
/**
* Information about the issuer.
*/
virtual QString issuerInfo(EntityInfoKey key) const;
/**
* Information about the subject
*/
virtual QString subjectInfo(EntityInfoKey key) const;
/**
* The date-time when certificate becomes valid.
*/
virtual QDateTime validityStart() const;
/**
* The date-time when certificate expires.
*/
virtual QDateTime validityEnd() const;
/**
* The uses allowed for the certificate.
*/
virtual KeyUsageExtensions keyUsageExtensions() const;
/**
* The public key value.
*/
virtual QByteArray publicKey() const;
/**
* The public key type.
*/
virtual PublicKeyType publicKeyType() const;
/**
* The strength of public key in bits.
*/
virtual int publicKeyStrength() const;
/**
* Returns true if certificate is self-signed otherwise returns false.
*/
virtual bool isSelfSigned() const;
/**
* The DER encoded certificate.
*/
virtual QByteArray certificateData() const;
protected:
friend class SignatureInfo;
CertificateInfo();
private:
Q_DISABLE_COPY( CertificateInfo )
};
/**
* @short A helper class to store information about digital signature
*/
class OKULARCORE_EXPORT SignatureInfo
{
public:
/**
* The verfication result of the signature.
*/
enum SignatureStatus
{
SignatureStatusUnknown, ///< The signature status is unknown for some reason.
SignatureValid, ///< The signature is cryptographically valid.
SignatureInvalid, ///< The signature is cryptographically invalid.
SignatureDigestMismatch, ///< The document content was changed after the signature was applied.
SignatureDecodingError, ///< The signature CMS/PKCS7 structure is malformed.
SignatureGenericError, ///< The signature could not be verified.
SignatureNotFound, ///< The requested signature is not present in the document.
SignatureNotVerified ///< The signature is not yet verified.
};
/**
* The verification result of the certificate.
*/
enum CertificateStatus
{
CertificateStatusUnknown, ///< The certificate status is unknown for some reason.
CertificateTrusted, ///< The certificate is considered trusted.
CertificateUntrustedIssuer, ///< The issuer of this certificate has been marked as untrusted by the user.
CertificateUnknownIssuer, ///< The certificate trust chain has not finished in a trusted root certificate.
CertificateRevoked, ///< The certificate was revoked by the issuing certificate authority.
CertificateExpired, ///< The signing time is outside the validity bounds of this certificate.
CertificateGenericError, ///< The certificate could not be verified.
CertificateNotVerified ///< The certificate is not yet verified.
};
/**
* The hash algorithm of the signature
*/
enum HashAlgorithm
{
HashAlgorithmUnknown,
HashAlgorithmMd2,
HashAlgorithmMd5,
HashAlgorithmSha1,
HashAlgorithmSha256,
HashAlgorithmSha384,
HashAlgorithmSha512,
HashAlgorithmSha224
};
/**
* Destructor.
*/
virtual ~SignatureInfo();
/**
* The signature status of the signature.
*/
virtual SignatureStatus signatureStatus() const;
/**
* The certificate status of the signature.
*/
virtual CertificateStatus certificateStatus() const;
/**
* The signer subject common name associated with the signature.
*/
virtual QString signerName() const;
/**
* The signer subject distinguished name associated with the signature.
*/
virtual QString signerSubjectDN() const;
/**
* Get signing location.
*/
virtual QString location() const;
/**
* Get signing reason.
*/
virtual QString reason() const;
/**
* The the hash algorithm used for the signature.
*/
virtual HashAlgorithm hashAlgorithm() const;
/**
* The signing time associated with the signature.
*/
virtual QDateTime signingTime() const;
/**
* Get the signature binary data.
*/
virtual QByteArray signature() const;
/**
* Get the bounds of the ranges of the document which are signed.
*/
virtual QList<qint64> signedRangeBounds() const;
/**
* Checks whether the signature authenticates the total document
* except for the signature itself.
*/
virtual bool signsTotalDocument() const;
/**
* Get certificate details.
*/
virtual const CertificateInfo &certificateInfo() const;
protected:
SignatureInfo();
private:
Q_DISABLE_COPY( SignatureInfo )
};
}
#endif