summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSune Vuorela <sune@vuorela.dk>2024-04-21 17:31:19 +0000
committerAlbert Astals Cid <aacid@kde.org>2024-04-21 17:31:19 +0000
commitd40bb7e308c9e3299e50d3e2880229cd6272587e (patch)
tree3b2bdeb5f4367f6bbd9ad749f9d7062ebaa75f63
parente6879d35f23e436b4fc1a55b957998a834cf1691 (diff)
Async api for certificate validation
-rw-r--r--glib/poppler-form-field.cc7
-rw-r--r--poppler/CryptoSignBackend.h6
-rw-r--r--poppler/Form.cc51
-rw-r--r--poppler/Form.h23
-rw-r--r--poppler/GPGMECryptoSignBackend.cc77
-rw-r--r--poppler/GPGMECryptoSignBackend.h6
-rw-r--r--poppler/NSSCryptoSignBackend.cc63
-rw-r--r--poppler/NSSCryptoSignBackend.h7
-rw-r--r--poppler/SignatureInfo.cc10
-rw-r--r--poppler/SignatureInfo.h3
-rw-r--r--qt5/src/poppler-form.cc98
-rw-r--r--qt5/src/poppler-form.h63
-rw-r--r--qt5/tests/check_signature_basics.cpp6
-rw-r--r--qt5/tests/poppler-forms.cpp7
-rw-r--r--qt6/src/poppler-form.cc98
-rw-r--r--qt6/src/poppler-form.h63
-rw-r--r--qt6/tests/check_signature_basics.cpp6
-rw-r--r--qt6/tests/poppler-forms.cpp2
-rw-r--r--utils/pdfsig.cc15
19 files changed, 477 insertions, 134 deletions
diff --git a/glib/poppler-form-field.cc b/glib/poppler-form-field.cc
index ccb1a407..8f4a7fdc 100644
--- a/glib/poppler-form-field.cc
+++ b/glib/poppler-form-field.cc
@@ -449,8 +449,9 @@ static PopplerSignatureInfo *_poppler_form_field_signature_validate(PopplerFormF
sig_field = static_cast<FormFieldSignature *>(field->widget->getField());
- sig_info = sig_field->validateSignature(flags & POPPLER_SIGNATURE_VALIDATION_FLAG_VALIDATE_CERTIFICATE, force_revalidation, -1, flags & POPPLER_SIGNATURE_VALIDATION_FLAG_WITHOUT_OCSP_REVOCATION_CHECK,
- flags & POPPLER_SIGNATURE_VALIDATION_FLAG_USE_AIA_CERTIFICATE_FETCH);
+ sig_info = sig_field->validateSignatureAsync(flags & POPPLER_SIGNATURE_VALIDATION_FLAG_VALIDATE_CERTIFICATE, force_revalidation, -1, flags & POPPLER_SIGNATURE_VALIDATION_FLAG_WITHOUT_OCSP_REVOCATION_CHECK,
+ flags & POPPLER_SIGNATURE_VALIDATION_FLAG_USE_AIA_CERTIFICATE_FETCH, {});
+ CertificateValidationStatus certificateStatus = sig_field->validateSignatureResult();
poppler_sig_info = g_new0(PopplerSignatureInfo, 1);
switch (sig_info->getSignatureValStatus()) {
@@ -477,7 +478,7 @@ static PopplerSignatureInfo *_poppler_form_field_signature_validate(PopplerFormF
break;
}
- switch (sig_info->getCertificateValStatus()) {
+ switch (certificateStatus) {
case CERTIFICATE_TRUSTED:
poppler_sig_info->cert_status = POPPLER_CERTIFICATE_TRUSTED;
break;
diff --git a/poppler/CryptoSignBackend.h b/poppler/CryptoSignBackend.h
index 9c79a24d..e9405253 100644
--- a/poppler/CryptoSignBackend.h
+++ b/poppler/CryptoSignBackend.h
@@ -14,6 +14,7 @@
#include <memory>
#include <chrono>
#include <optional>
+#include <functional>
#include "HashAlgorithm.h"
#include "CertificateInfo.h"
#include "SignatureInfo.h"
@@ -38,7 +39,10 @@ public:
virtual std::string getSignerName() const = 0;
virtual std::string getSignerSubjectDN() const = 0;
virtual HashAlgorithm getHashAlgorithm() const = 0;
- virtual CertificateValidationStatus validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) = 0;
+
+ // Blocking if doneCallback to validateCertificateAsync has not yet been called
+ virtual CertificateValidationStatus validateCertificateResult() = 0;
+ virtual void validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function<void()> &doneCallback) = 0;
virtual std::unique_ptr<X509CertificateInfo> getCertificateInfo() const = 0;
virtual ~VerificationInterface();
VerificationInterface() = default;
diff --git a/poppler/Form.cc b/poppler/Form.cc
index 985f4870..7e283638 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -575,9 +575,14 @@ const GooString *FormWidgetSignature::getSignature() const
return static_cast<FormFieldSignature *>(field)->getSignature();
}
-SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA)
+SignatureInfo *FormWidgetSignature::validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function<void()> &doneCallback)
{
- return static_cast<FormFieldSignature *>(field)->validateSignature(doVerifyCert, forceRevalidation, validationTime, ocspRevocationCheck, enableAIA);
+ return static_cast<FormFieldSignature *>(field)->validateSignatureAsync(doVerifyCert, forceRevalidation, validationTime, ocspRevocationCheck, enableAIA, doneCallback);
+}
+
+CertificateValidationStatus FormWidgetSignature::validateSignatureResult()
+{
+ return static_cast<FormFieldSignature *>(field)->validateSignatureResult();
}
// update hash with the specified range of data from the file
@@ -2388,37 +2393,52 @@ void FormWidgetSignature::setSignatureType(FormSignatureType fst)
static_cast<FormFieldSignature *>(field)->setSignatureType(fst);
}
-SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA)
+SignatureInfo *FormFieldSignature::validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function<void()> &doneCallback)
{
auto backend = CryptoSign::Factory::createActive();
if (!backend) {
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
if (signature_info->getSignatureValStatus() != SIGNATURE_NOT_VERIFIED && !forceRevalidation) {
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
if (signature == nullptr) {
error(errSyntaxError, 0, "Invalid or missing Signature string");
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
if (!byte_range.isArray()) {
error(errSyntaxError, 0, "Invalid or missing ByteRange array");
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
int arrayLen = byte_range.arrayGetLength();
if (arrayLen < 2) {
error(errSyntaxError, 0, "Too few elements in ByteRange array");
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
const int signature_len = signature->getLength();
std::vector<unsigned char> signatureData(signature_len);
memcpy(signatureData.data(), signature->c_str(), signature_len);
- auto signature_handler = backend->createVerificationHandler(std::move(signatureData));
+ signature_handler = backend->createVerificationHandler(std::move(signatureData));
Goffset fileLength = doc->getBaseStream()->getLength();
for (int i = 0; i < arrayLen / 2; i++) {
@@ -2427,6 +2447,9 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
if (!offsetObj.isIntOrInt64() || !lenObj.isIntOrInt64()) {
error(errSyntaxError, 0, "Illegal values in ByteRange array");
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
@@ -2435,6 +2458,9 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
if (offset < 0 || offset >= fileLength || len < 0 || len > fileLength || offset + len > fileLength) {
error(errSyntaxError, 0, "Illegal values in ByteRange array");
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
@@ -2444,6 +2470,9 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
if (!signature_info->isSubfilterSupported()) {
error(errUnimplemented, 0, "Unable to validate this type of signature");
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
const SignatureValidationStatus sig_val_state = signature_handler->validateSignature();
@@ -2460,15 +2489,25 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
signature_info->setCertificateInfo(signature_handler->getCertificateInfo());
if (sig_val_state != SIGNATURE_VALID || !doVerifyCert) {
+ if (doneCallback) {
+ doneCallback();
+ }
return signature_info;
}
- const CertificateValidationStatus cert_val_state = signature_handler->validateCertificate(std::chrono::system_clock::from_time_t(validationTime), ocspRevocationCheck, enableAIA);
- signature_info->setCertificateValStatus(cert_val_state);
+ signature_handler->validateCertificateAsync(std::chrono::system_clock::from_time_t(validationTime), ocspRevocationCheck, enableAIA, doneCallback);
return signature_info;
}
+CertificateValidationStatus FormFieldSignature::validateSignatureResult()
+{
+ if (!signature_handler) {
+ return CERTIFICATE_GENERIC_ERROR;
+ }
+ return signature_handler->validateCertificateResult();
+}
+
std::vector<Goffset> FormFieldSignature::getSignedRangeBounds() const
{
std::vector<Goffset> range_vec;
diff --git a/poppler/Form.h b/poppler/Form.h
index da08c92f..e9a83bab 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -38,12 +38,14 @@
#include "CharTypes.h"
#include "Object.h"
#include "poppler_private_export.h"
+#include "SignatureInfo.h"
#include <ctime>
#include <optional>
#include <set>
#include <vector>
+#include <functional>
class GooString;
class Array;
@@ -54,7 +56,6 @@ class Annots;
class LinkAction;
class GfxResources;
class PDFDoc;
-class SignatureInfo;
class X509CertificateInfo;
namespace CryptoSign {
class VerificationInterface;
@@ -297,7 +298,20 @@ public:
void setSignatureType(FormSignatureType fst);
// Use -1 for now as validationTime
- SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA);
+ // ocspRevocation and aiafetch might happen async in the Background
+ // doneCallback will be invoked once there is a result
+ // Note: Validation callback will likely happen from an auxillary
+ // thread and it is the caller of this method who is responsible
+ // for moving back to the main thread
+ // For synchronous code, don't provide validation callback
+ // and just call validateSignatureResult afterwards
+ // The returned SignatureInfo from this method does
+ // not have validated the certificate.
+ SignatureInfo *validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function<void()> &doneCallback);
+
+ /// Waits, if needed, on validation callback and
+ /// returns a signatureinfo with validated certificates
+ CertificateValidationStatus validateSignatureResult();
// returns a list with the boundaries of the signed ranges
// the elements of the list are of type Goffset
@@ -605,7 +619,9 @@ public:
FormFieldSignature(PDFDoc *docA, Object &&dict, const Ref ref, FormField *parent, std::set<int> *usedParents);
// Use -1 for now as validationTime
- SignatureInfo *validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA);
+ SignatureInfo *validateSignatureAsync(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA, const std::function<void()> &doneCallback);
+
+ CertificateValidationStatus validateSignatureResult();
// returns a list with the boundaries of the signed ranges
// the elements of the list are of type Goffset
@@ -653,6 +669,7 @@ private:
double customAppearanceLeftFontSize = 20;
Ref imageResource = Ref::INVALID();
std::unique_ptr<X509CertificateInfo> certificate_info;
+ std::unique_ptr<CryptoSign::VerificationInterface> signature_handler;
void print(int indent) override;
};
diff --git a/poppler/GPGMECryptoSignBackend.cc b/poppler/GPGMECryptoSignBackend.cc
index b89f61fc..c60bc5ad 100644
--- a/poppler/GPGMECryptoSignBackend.cc
+++ b/poppler/GPGMECryptoSignBackend.cc
@@ -347,32 +347,77 @@ std::chrono::system_clock::time_point GpgSignatureVerification::getSigningTime()
return std::chrono::system_clock::from_time_t(signature->creationTime());
}
-CertificateValidationStatus GpgSignatureVerification::validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch)
+void GpgSignatureVerification::validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function<void()> &doneFunction)
{
+ cachedValidationStatus.reset();
if (!gpgResult) {
- return CERTIFICATE_NOT_VERIFIED;
+ validationStatus = std::async([doneFunction]() {
+ if (doneFunction) {
+ doneFunction();
+ }
+ return CERTIFICATE_NOT_VERIFIED;
+ });
+ return;
}
if (gpgResult->error()) {
- return CERTIFICATE_GENERIC_ERROR;
+ validationStatus = std::async([doneFunction]() {
+ if (doneFunction) {
+ doneFunction();
+ }
+ return CERTIFICATE_GENERIC_ERROR;
+ });
+ return;
}
const auto signature = getSignature(gpgResult.value(), 0);
if (!signature) {
- return CERTIFICATE_GENERIC_ERROR;
- }
- const auto offline = gpgContext->offline();
- gpgContext->setOffline((!ocspRevocationCheck) || useAIACertFetch);
- const auto key = signature->key(true, true);
- gpgContext->setOffline(offline);
- if (key.isExpired()) {
- return CERTIFICATE_EXPIRED;
- }
- if (key.isRevoked()) {
- return CERTIFICATE_REVOKED;
+ validationStatus = std::async([doneFunction]() {
+ if (doneFunction) {
+ doneFunction();
+ }
+ return CERTIFICATE_GENERIC_ERROR;
+ });
+ return;
+ }
+ std::string keyFP = fromCharPtr(signature->key().primaryFingerprint());
+ validationStatus = std::async([keyFP = std::move(keyFP), doneFunction, ocspRevocationCheck, useAIACertFetch]() {
+ auto context = GpgME::Context::create(GpgME::CMS);
+ context->setOffline((!ocspRevocationCheck) || useAIACertFetch);
+ context->setKeyListMode(GpgME::KeyListMode::Local | GpgME::KeyListMode::Validate);
+ GpgME::Error e;
+ const auto key = context->key(keyFP.c_str(), e, false);
+ if (doneFunction) {
+ doneFunction();
+ }
+ if (e.isCanceled()) {
+ return CERTIFICATE_NOT_VERIFIED;
+ }
+ if (e) {
+ return CERTIFICATE_GENERIC_ERROR;
+ }
+ if (key.isExpired()) {
+ return CERTIFICATE_EXPIRED;
+ }
+ if (key.isRevoked()) {
+ return CERTIFICATE_REVOKED;
+ }
+ if (key.isBad()) {
+ return CERTIFICATE_NOT_VERIFIED;
+ }
+ return CERTIFICATE_TRUSTED;
+ });
+}
+
+CertificateValidationStatus GpgSignatureVerification::validateCertificateResult()
+{
+ if (cachedValidationStatus) {
+ return cachedValidationStatus.value();
}
- if (key.isBad()) {
+ if (!validationStatus.valid()) {
return CERTIFICATE_NOT_VERIFIED;
}
- return CERTIFICATE_TRUSTED;
+ validationStatus.wait();
+ cachedValidationStatus = validationStatus.get();
+ return cachedValidationStatus.value();
}
SignatureValidationStatus GpgSignatureVerification::validateSignature()
diff --git a/poppler/GPGMECryptoSignBackend.h b/poppler/GPGMECryptoSignBackend.h
index 9cab07a7..c7ed4510 100644
--- a/poppler/GPGMECryptoSignBackend.h
+++ b/poppler/GPGMECryptoSignBackend.h
@@ -11,6 +11,7 @@
#include <gpgme++/data.h>
#include <gpgme++/context.h>
#include <optional>
+#include <future>
class GpgSignatureBackend : public CryptoSign::Backend
{
@@ -46,7 +47,8 @@ public:
std::string getSignerName() const final;
std::string getSignerSubjectDN() const final;
HashAlgorithm getHashAlgorithm() const final;
- CertificateValidationStatus validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) final;
+ CertificateValidationStatus validateCertificateResult() final;
+ void validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function<void()> &doneCallback) final;
std::unique_ptr<X509CertificateInfo> getCertificateInfo() const final;
private:
@@ -54,4 +56,6 @@ private:
GpgME::Data signatureData;
GpgME::Data signedData;
std::optional<GpgME::VerificationResult> gpgResult;
+ std::future<CertificateValidationStatus> validationStatus;
+ std::optional<CertificateValidationStatus> cachedValidationStatus;
};
diff --git a/poppler/NSSCryptoSignBackend.cc b/poppler/NSSCryptoSignBackend.cc
index dd61d4ed..fe13c213 100644
--- a/poppler/NSSCryptoSignBackend.cc
+++ b/poppler/NSSCryptoSignBackend.cc
@@ -966,12 +966,19 @@ SignatureValidationStatus NSSSignatureVerification::validateSignature()
}
}
-CertificateValidationStatus NSSSignatureVerification::validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch)
+void NSSSignatureVerification::validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function<void()> &doneCallback)
{
+ cachedValidationStatus.reset();
CERTCertificate *cert;
if (!CMSSignerInfo) {
- return CERTIFICATE_GENERIC_ERROR;
+ validationStatus = std::async([doneCallback]() {
+ if (doneCallback) {
+ doneCallback();
+ }
+ return CERTIFICATE_GENERIC_ERROR;
+ });
+ return;
}
if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr) {
@@ -1001,25 +1008,49 @@ CertificateValidationStatus NSSSignatureVerification::validateCertificate(std::c
CERT_PKIXVerifyCert(cert, certificateUsageEmailSigner, inParams, nullptr, CMSSignerInfo->cmsg->pwfn_arg);
- switch (PORT_GetError()) {
- // 0 not defined in SECErrorCodes, it means success for this purpose.
- case 0:
- return CERTIFICATE_TRUSTED;
+ // Here we are just faking the asynchronousness. It should
+ // somehow be the call to CERT_PXIXVerifyCert that would
+ // be put in the thread, but I'm not sure about all of the
+ // thread safety of nss.
- case SEC_ERROR_UNKNOWN_ISSUER:
- return CERTIFICATE_UNKNOWN_ISSUER;
+ validationStatus = std::async([result = PORT_GetError(), doneCallback]() {
+ if (doneCallback) {
+ doneCallback();
+ }
- case SEC_ERROR_UNTRUSTED_ISSUER:
- return CERTIFICATE_UNTRUSTED_ISSUER;
+ switch (result) {
+ // 0 not defined in SECErrorCodes, it means success for this purpose.
+ case 0:
+ return CERTIFICATE_TRUSTED;
- case SEC_ERROR_REVOKED_CERTIFICATE:
- return CERTIFICATE_REVOKED;
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ return CERTIFICATE_UNKNOWN_ISSUER;
- case SEC_ERROR_EXPIRED_CERTIFICATE:
- return CERTIFICATE_EXPIRED;
- }
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ return CERTIFICATE_UNTRUSTED_ISSUER;
+
+ case SEC_ERROR_REVOKED_CERTIFICATE:
+ return CERTIFICATE_REVOKED;
+
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ return CERTIFICATE_EXPIRED;
+ }
+
+ return CERTIFICATE_GENERIC_ERROR;
+ });
+}
- return CERTIFICATE_GENERIC_ERROR;
+CertificateValidationStatus NSSSignatureVerification::validateCertificateResult()
+{
+ if (cachedValidationStatus) {
+ return cachedValidationStatus.value();
+ }
+ if (!validationStatus.valid()) {
+ return CERTIFICATE_NOT_VERIFIED;
+ }
+ validationStatus.wait();
+ cachedValidationStatus = validationStatus.get();
+ return cachedValidationStatus.value();
}
std::optional<GooString> NSSSignatureCreation::signDetached(const std::string &password)
diff --git a/poppler/NSSCryptoSignBackend.h b/poppler/NSSCryptoSignBackend.h
index 978068ed..1a724821 100644
--- a/poppler/NSSCryptoSignBackend.h
+++ b/poppler/NSSCryptoSignBackend.h
@@ -30,6 +30,7 @@
#include <vector>
#include <functional>
#include <memory>
+#include <future>
/* NSPR Headers */
#include <nspr.h>
@@ -78,7 +79,9 @@ public:
std::string getSignerName() const final;
std::string getSignerSubjectDN() const final;
// Use -1 as validation_time for now
- CertificateValidationStatus validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) final;
+
+ CertificateValidationStatus validateCertificateResult() final;
+ void validateCertificateAsync(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch, const std::function<void()> &doneCallback) final;
std::unique_ptr<X509CertificateInfo> getCertificateInfo() const final;
void addData(unsigned char *data_block, int data_len) final;
HashAlgorithm getHashAlgorithm() const final;
@@ -93,6 +96,8 @@ private:
NSSCMSSignerInfo *CMSSignerInfo;
SECItem CMSitem;
std::unique_ptr<HashContext> hashContext;
+ std::future<CertificateValidationStatus> validationStatus;
+ std::optional<CertificateValidationStatus> cachedValidationStatus;
};
class NSSSignatureCreation final : public CryptoSign::SigningInterface
diff --git a/poppler/SignatureInfo.cc b/poppler/SignatureInfo.cc
index bce00bdc..7b7ce199 100644
--- a/poppler/SignatureInfo.cc
+++ b/poppler/SignatureInfo.cc
@@ -36,11 +36,6 @@ SignatureValidationStatus SignatureInfo::getSignatureValStatus() const
return sig_status;
}
-CertificateValidationStatus SignatureInfo::getCertificateValStatus() const
-{
- return cert_status;
-}
-
std::string SignatureInfo::getSignerName() const
{
return signer_name;
@@ -83,11 +78,6 @@ void SignatureInfo::setSignatureValStatus(enum SignatureValidationStatus sig_val
sig_status = sig_val_status;
}
-void SignatureInfo::setCertificateValStatus(enum CertificateValidationStatus cert_val_status)
-{
- cert_status = cert_val_status;
-}
-
void SignatureInfo::setSignerName(const std::string &signerName)
{
signer_name = signerName;
diff --git a/poppler/SignatureInfo.h b/poppler/SignatureInfo.h
index d389ae24..0e962fb7 100644
--- a/poppler/SignatureInfo.h
+++ b/poppler/SignatureInfo.h
@@ -62,7 +62,6 @@ public:
/* GETTERS */
SignatureValidationStatus getSignatureValStatus() const;
- CertificateValidationStatus getCertificateValStatus() const;
std::string getSignerName() const;
std::string getSubjectDN() const;
const GooString &getLocation() const;
@@ -74,7 +73,6 @@ public:
/* SETTERS */
void setSignatureValStatus(enum SignatureValidationStatus);
- void setCertificateValStatus(enum CertificateValidationStatus);
void setSignerName(const std::string &);
void setSubjectDN(const std::string &);
void setLocation(const GooString *);
@@ -86,7 +84,6 @@ public:
private:
SignatureValidationStatus sig_status = SIGNATURE_NOT_VERIFIED;
- CertificateValidationStatus cert_status = CERTIFICATE_NOT_VERIFIED;
std::unique_ptr<X509CertificateInfo> cert_info;
std::string signer_name;
std::string subject_dn;
diff --git a/qt5/src/poppler-form.cc b/qt5/src/poppler-form.cc
index 08c91c33..e48e2e18 100644
--- a/qt5/src/poppler-form.cc
+++ b/qt5/src/poppler-form.cc
@@ -975,7 +975,9 @@ FormFieldSignature::SignatureType FormFieldSignature::signatureType() const
SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const
{
- return validate(opt, QDateTime());
+ auto tempResult = validateAsync(opt);
+ tempResult.first.d_ptr->certificate_status = validateResult();
+ return tempResult.first;
}
static CertificateInfo::KeyLocation fromPopplerCore(KeyLocation location)
@@ -1037,12 +1039,29 @@ static CertificateInfoPrivate *createCertificateInfoPrivate(const X509Certificat
return certPriv;
}
-SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const
+static SignatureValidationInfo::CertificateStatus fromInternal(CertificateValidationStatus status)
{
- FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
- const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1;
- SignatureInfo *si = fws->validateSignature(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch);
+ switch (status) {
+ case CERTIFICATE_TRUSTED:
+ return SignatureValidationInfo::CertificateTrusted;
+ case CERTIFICATE_UNTRUSTED_ISSUER:
+ return SignatureValidationInfo::CertificateUntrustedIssuer;
+ case CERTIFICATE_UNKNOWN_ISSUER:
+ return SignatureValidationInfo::CertificateUnknownIssuer;
+ case CERTIFICATE_REVOKED:
+ return SignatureValidationInfo::CertificateRevoked;
+ case CERTIFICATE_EXPIRED:
+ return SignatureValidationInfo::CertificateExpired;
+ default:
+ case CERTIFICATE_GENERIC_ERROR:
+ return SignatureValidationInfo::CertificateGenericError;
+ case CERTIFICATE_NOT_VERIFIED:
+ return SignatureValidationInfo::CertificateNotVerified;
+ }
+}
+static SignatureValidationInfo fromInternal(SignatureInfo *si, FormWidgetSignature *fws)
+{
// get certificate info
const X509CertificateInfo *ci = si->getCertificateInfo();
CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci);
@@ -1072,30 +1091,7 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &v
priv->signature_status = SignatureValidationInfo::SignatureNotVerified;
break;
}
- switch (si->getCertificateValStatus()) {
- case CERTIFICATE_TRUSTED:
- priv->certificate_status = SignatureValidationInfo::CertificateTrusted;
- break;
- case CERTIFICATE_UNTRUSTED_ISSUER:
- priv->certificate_status = SignatureValidationInfo::CertificateUntrustedIssuer;
- break;
- case CERTIFICATE_UNKNOWN_ISSUER:
- priv->certificate_status = SignatureValidationInfo::CertificateUnknownIssuer;
- break;
- case CERTIFICATE_REVOKED:
- priv->certificate_status = SignatureValidationInfo::CertificateRevoked;
- break;
- case CERTIFICATE_EXPIRED:
- priv->certificate_status = SignatureValidationInfo::CertificateExpired;
- break;
- default:
- case CERTIFICATE_GENERIC_ERROR:
- priv->certificate_status = SignatureValidationInfo::CertificateGenericError;
- break;
- case CERTIFICATE_NOT_VERIFIED:
- priv->certificate_status = SignatureValidationInfo::CertificateNotVerified;
- break;
- }
+ priv->certificate_status = SignatureValidationInfo::CertificateVerificationInProgress;
priv->signer_name = QString::fromStdString(si->getSignerName());
priv->signer_subject_dn = QString::fromStdString(si->getSubjectDN());
priv->hash_algorithm = si->getHashAlgorithm();
@@ -1117,6 +1113,50 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &v
return SignatureValidationInfo(priv);
}
+SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const
+{
+ auto tempResult = validateAsync(static_cast<ValidateOptions>(opt), validationTime);
+ tempResult.first.d_ptr->certificate_status = validateResult();
+ return tempResult.first;
+}
+
+class AsyncObjectPrivate
+{ /*Currently unused. Created for abi future proofing*/
+};
+
+AsyncObject::AsyncObject() : QObject(nullptr), d {} { }
+
+AsyncObject::~AsyncObject() = default;
+
+std::pair<SignatureValidationInfo, std::shared_ptr<Poppler::AsyncObject>> FormFieldSignature::validateAsync(ValidateOptions opt, const QDateTime &validationTime) const
+{
+ auto object = std::make_shared<AsyncObject>();
+ FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
+ const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1;
+ SignatureInfo *si = fws->validateSignatureAsync(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch,
+ [obj = std::weak_ptr<AsyncObject>(object)]() {
+ if (auto l = obj.lock()) {
+ // We need to roundtrip over the eventloop
+ // to ensure callers have a chance of connecting to AsyncObject::done
+ QMetaObject::invokeMethod(
+ l.get(),
+ [innerObj = std::weak_ptr<AsyncObject>(l)]() {
+ if (auto innerLocked = innerObj.lock()) {
+ emit innerLocked->done();
+ }
+ },
+ Qt::QueuedConnection);
+ }
+ });
+
+ return { fromInternal(si, fws), object };
+}
+
+SignatureValidationInfo::CertificateStatus FormFieldSignature::validateResult() const
+{
+ return fromInternal(static_cast<FormWidgetSignature *>(m_formData->fm)->validateSignatureResult());
+}
+
FormFieldSignature::SigningResult FormFieldSignature::sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) const
{
FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
diff --git a/qt5/src/poppler-form.h b/qt5/src/poppler-form.h
index ad4fa2c3..7a7f9d80 100644
--- a/qt5/src/poppler-form.h
+++ b/qt5/src/poppler-form.h
@@ -687,7 +687,8 @@ public:
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.
+ CertificateNotVerified, ///< The certificate is not yet verified.
+ CertificateVerificationInProgress ///< The certificate is not yet verified but is in progress in the background. See \ref validateAsync \since 24.05
};
/**
@@ -785,11 +786,33 @@ public:
private:
Q_DECLARE_PRIVATE(SignatureValidationInfo)
-
+ friend class FormFieldSignature;
QSharedPointer<SignatureValidationInfoPrivate> d_ptr;
};
/**
+ * Object help waiting for some async event
+ *
+ * \since 24.05
+ */
+class AsyncObjectPrivate;
+class POPPLER_QT5_EXPORT AsyncObject : public QObject // clazy:exclude=ctor-missing-parent-argument
+{
+ Q_OBJECT
+public:
+ /* Constructor. On purpose not having a QObject parameter
+ It will be returned by shared_ptr or unique_ptr
+ */
+ AsyncObject();
+ ~AsyncObject() override;
+Q_SIGNALS:
+ void done();
+public Q_SLOTS:
+private:
+ std::unique_ptr<AsyncObjectPrivate> d;
+};
+
+/**
A form field that represents a signature.
\since 0.51
@@ -844,8 +867,10 @@ public:
requiring network access, AIAFetch and OCSP,
can be toggled individually. In case of the GPG backend, if either
OCSP is used or AIAFetch is used, the other one is also used.
+
+ \deprecated Please rewrite to the async version, that allows the network traffic part of fetching to happen in the background
*/
- SignatureValidationInfo validate(ValidateOptions opt) const;
+ POPPLER_QT5_DEPRECATED SignatureValidationInfo validate(ValidateOptions opt) const;
/**
Validate the signature with @p validationTime as validation time.
@@ -859,8 +884,38 @@ public:
requiring network access, AIAFetch and OCSP,
can be toggled individually. In case of the GPG backend, if either
OCSP is used or AIAFetch is used, the other one is also used.
+
+ \deprecated Please rewrite to the async version, that allows the network traffic part of fetching to happen in the background
+ */
+ POPPLER_QT5_DEPRECATED SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const;
+
+ /**
+ Validate the signature with @p validationTime as validation time.
+
+ Reset signature validatation info of scoped instance.
+
+ \since 24.05
+
+ \note depending on the backend, some options are only
+ partially respected. In case of the NSS backend, the two options
+ requiring network access, AIAFetch and OCSP,
+ can be toggled individually. In case of the GPG backend, if either
+ OCSP is used or AIAFetch is used, the other one is also used.
+
+ \note certificate validation will have started when this function return. See \ref validateResult on how to get certifcate validation
+ \note connections to \ref AsyncObject must happen by the caller
+ before returning control to the event loop, else signals is not guaranteed to be delivered
+ */
+ std::pair<SignatureValidationInfo, std::shared_ptr<AsyncObject>> validateAsync(ValidateOptions opt, const QDateTime &validationTime = {}) const;
+
+ /**
+ * \return the updated signature validation info from validateAsync
+ * \note that this function will block if the result is not yet ready.
+ * Wait for the \ref AsyncObject::done signal to avoid this function blocking on an inconvenient time
+ *
+ * \since 24.05
*/
- SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const;
+ SignatureValidationInfo::CertificateStatus validateResult() const;
/**
* \since 22.02
diff --git a/qt5/tests/check_signature_basics.cpp b/qt5/tests/check_signature_basics.cpp
index 970b127b..0816db6f 100644
--- a/qt5/tests/check_signature_basics.cpp
+++ b/qt5/tests/check_signature_basics.cpp
@@ -112,7 +112,8 @@ void TestSignatureBasics::testSignerInfo()
auto signatureFields = doc->getSignatureFields();
QCOMPARE(signatureFields[0]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature0_B_" });
QCOMPARE(signatureFields[0]->getSignatureType(), ETSI_CAdES_detached);
- auto siginfo0 = signatureFields[0]->validateSignature(false, false, -1 /* now */, false, false);
+ auto siginfo0 = signatureFields[0]->validateSignatureAsync(false, false, -1 /* now */, false, false, {});
+ signatureFields[0]->validateSignatureResult();
#ifdef ENABLE_SIGNATURES
QCOMPARE(siginfo0->getSignerName(), std::string { "Koch, Werner" });
QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Sha256);
@@ -125,7 +126,8 @@ void TestSignatureBasics::testSignerInfo()
QCOMPARE(signatureFields[1]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature1_B_" });
QCOMPARE(signatureFields[1]->getSignatureType(), ETSI_CAdES_detached);
- auto siginfo1 = signatureFields[1]->validateSignature(false, false, -1 /* now */, false, false);
+ auto siginfo1 = signatureFields[1]->validateSignatureAsync(false, false, -1 /* now */, false, false, {});
+ signatureFields[1]->validateSignatureResult();
#ifdef ENABLE_SIGNATURES
QCOMPARE(siginfo1->getSignerName(), std::string { "Koch, Werner" });
QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Sha256);
diff --git a/qt5/tests/poppler-forms.cpp b/qt5/tests/poppler-forms.cpp
index 5950969a..8d927e84 100644
--- a/qt5/tests/poppler-forms.cpp
+++ b/qt5/tests/poppler-forms.cpp
@@ -123,6 +123,8 @@ static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationI
case Poppler::SignatureValidationInfo::CertificateNotVerified:
out << "NotVerifiedYet";
break;
+ case Poppler::SignatureValidationInfo::CertificateVerificationInProgress:
+ out << "InProgress";
}
return out;
}
@@ -252,9 +254,10 @@ int main(int argc, char **argv)
case Poppler::FormField::FormSignature: {
const Poppler::FormFieldSignature *signatureForm = static_cast<const Poppler::FormFieldSignature *>(form);
- const Poppler::SignatureValidationInfo svi = signatureForm->validate(Poppler::FormFieldSignature::ValidateVerifyCertificate);
+ const Poppler::SignatureValidationInfo svi = signatureForm->validateAsync(Poppler::FormFieldSignature::ValidateVerifyCertificate).first;
+ const Poppler::SignatureValidationInfo::CertificateStatus certStatus = signatureForm->validateResult();
std::cout << "\t\t\tSignatureStatus: " << svi.signatureStatus() << std::endl;
- std::cout << "\t\t\tCertificateStatus: " << svi.certificateStatus() << std::endl;
+ std::cout << "\t\t\tCertificateStatus: " << certStatus << std::endl;
if (svi.signerName().isEmpty() == false) {
std::cout << "\t\t\tSignerName: " << svi.signerName() << std::endl;
} else {
diff --git a/qt6/src/poppler-form.cc b/qt6/src/poppler-form.cc
index 781e9546..70580daf 100644
--- a/qt6/src/poppler-form.cc
+++ b/qt6/src/poppler-form.cc
@@ -975,7 +975,9 @@ FormFieldSignature::SignatureType FormFieldSignature::signatureType() const
SignatureValidationInfo FormFieldSignature::validate(ValidateOptions opt) const
{
- return validate(opt, QDateTime());
+ auto tempResult = validateAsync(opt);
+ tempResult.first.d_ptr->certificate_status = validateResult();
+ return tempResult.first;
}
static CertificateInfo::KeyLocation fromPopplerCore(KeyLocation location)
@@ -1037,12 +1039,29 @@ static CertificateInfoPrivate *createCertificateInfoPrivate(const X509Certificat
return certPriv;
}
-SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const
+static SignatureValidationInfo::CertificateStatus fromInternal(CertificateValidationStatus status)
{
- FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
- const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1;
- SignatureInfo *si = fws->validateSignature(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch);
+ switch (status) {
+ case CERTIFICATE_TRUSTED:
+ return SignatureValidationInfo::CertificateTrusted;
+ case CERTIFICATE_UNTRUSTED_ISSUER:
+ return SignatureValidationInfo::CertificateUntrustedIssuer;
+ case CERTIFICATE_UNKNOWN_ISSUER:
+ return SignatureValidationInfo::CertificateUnknownIssuer;
+ case CERTIFICATE_REVOKED:
+ return SignatureValidationInfo::CertificateRevoked;
+ case CERTIFICATE_EXPIRED:
+ return SignatureValidationInfo::CertificateExpired;
+ default:
+ case CERTIFICATE_GENERIC_ERROR:
+ return SignatureValidationInfo::CertificateGenericError;
+ case CERTIFICATE_NOT_VERIFIED:
+ return SignatureValidationInfo::CertificateNotVerified;
+ }
+}
+static SignatureValidationInfo fromInternal(SignatureInfo *si, FormWidgetSignature *fws)
+{
// get certificate info
const X509CertificateInfo *ci = si->getCertificateInfo();
CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(ci);
@@ -1072,30 +1091,7 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &v
priv->signature_status = SignatureValidationInfo::SignatureNotVerified;
break;
}
- switch (si->getCertificateValStatus()) {
- case CERTIFICATE_TRUSTED:
- priv->certificate_status = SignatureValidationInfo::CertificateTrusted;
- break;
- case CERTIFICATE_UNTRUSTED_ISSUER:
- priv->certificate_status = SignatureValidationInfo::CertificateUntrustedIssuer;
- break;
- case CERTIFICATE_UNKNOWN_ISSUER:
- priv->certificate_status = SignatureValidationInfo::CertificateUnknownIssuer;
- break;
- case CERTIFICATE_REVOKED:
- priv->certificate_status = SignatureValidationInfo::CertificateRevoked;
- break;
- case CERTIFICATE_EXPIRED:
- priv->certificate_status = SignatureValidationInfo::CertificateExpired;
- break;
- default:
- case CERTIFICATE_GENERIC_ERROR:
- priv->certificate_status = SignatureValidationInfo::CertificateGenericError;
- break;
- case CERTIFICATE_NOT_VERIFIED:
- priv->certificate_status = SignatureValidationInfo::CertificateNotVerified;
- break;
- }
+ priv->certificate_status = SignatureValidationInfo::CertificateVerificationInProgress;
priv->signer_name = QString::fromStdString(si->getSignerName());
priv->signer_subject_dn = QString::fromStdString(si->getSubjectDN());
priv->hash_algorithm = si->getHashAlgorithm();
@@ -1117,6 +1113,50 @@ SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &v
return SignatureValidationInfo(priv);
}
+SignatureValidationInfo FormFieldSignature::validate(int opt, const QDateTime &validationTime) const
+{
+ auto tempResult = validateAsync(static_cast<ValidateOptions>(opt), validationTime);
+ tempResult.first.d_ptr->certificate_status = validateResult();
+ return tempResult.first;
+}
+
+class AsyncObjectPrivate
+{ /*Currently unused. Created for abi future proofing*/
+};
+
+AsyncObject::AsyncObject() : QObject(nullptr), d {} { }
+
+AsyncObject::~AsyncObject() = default;
+
+std::pair<SignatureValidationInfo, std::shared_ptr<Poppler::AsyncObject>> FormFieldSignature::validateAsync(ValidateOptions opt, const QDateTime &validationTime) const
+{
+ auto object = std::make_shared<AsyncObject>();
+ FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
+ const time_t validationTimeT = validationTime.isValid() ? validationTime.toSecsSinceEpoch() : -1;
+ SignatureInfo *si = fws->validateSignatureAsync(opt & ValidateVerifyCertificate, opt & ValidateForceRevalidation, validationTimeT, !(opt & ValidateWithoutOCSPRevocationCheck), opt & ValidateUseAIACertFetch,
+ [obj = std::weak_ptr<AsyncObject>(object)]() {
+ if (auto l = obj.lock()) {
+ // We need to roundtrip over the eventloop
+ // to ensure callers have a chance of connecting to AsyncObject::done
+ QMetaObject::invokeMethod(
+ l.get(),
+ [innerObj = std::weak_ptr<AsyncObject>(l)]() {
+ if (auto innerLocked = innerObj.lock()) {
+ emit innerLocked->done();
+ }
+ },
+ Qt::QueuedConnection);
+ }
+ });
+
+ return { fromInternal(si, fws), object };
+}
+
+SignatureValidationInfo::CertificateStatus FormFieldSignature::validateResult() const
+{
+ return fromInternal(static_cast<FormWidgetSignature *>(m_formData->fm)->validateSignatureResult());
+}
+
FormFieldSignature::SigningResult FormFieldSignature::sign(const QString &outputFileName, const PDFConverter::NewSignatureData &data) const
{
FormWidgetSignature *fws = static_cast<FormWidgetSignature *>(m_formData->fm);
diff --git a/qt6/src/poppler-form.h b/qt6/src/poppler-form.h
index 7bd4105d..9d2fef1a 100644
--- a/qt6/src/poppler-form.h
+++ b/qt6/src/poppler-form.h
@@ -651,7 +651,8 @@ public:
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.
+ CertificateNotVerified, ///< The certificate is not yet verified.
+ CertificateVerificationInProgress ///< The certificate is not yet verified but is in progress in the background. See \ref validateAsync \since 24.05
};
/**
@@ -740,11 +741,33 @@ public:
private:
Q_DECLARE_PRIVATE(SignatureValidationInfo)
-
+ friend class FormFieldSignature;
QSharedPointer<SignatureValidationInfoPrivate> d_ptr;
};
/**
+ * Object help waiting for some async event
+ *
+ * \since 24.05
+ */
+class AsyncObjectPrivate;
+class POPPLER_QT6_EXPORT AsyncObject : public QObject // clazy:exclude=ctor-missing-parent-argument
+{
+ Q_OBJECT
+public:
+ /* Constructor. On purpose not having a QObject parameter
+ It will be returned by shared_ptr or unique_ptr
+ */
+ AsyncObject();
+ ~AsyncObject() override;
+Q_SIGNALS:
+ void done();
+public Q_SLOTS:
+private:
+ std::unique_ptr<AsyncObjectPrivate> d;
+};
+
+/**
A form field that represents a signature.
*/
class POPPLER_QT6_EXPORT FormFieldSignature : public FormField
@@ -795,8 +818,10 @@ public:
requiring network access, AIAFetch and OCSP,
can be toggled individually. In case of the GPG backend, if either
OCSP is used or AIAFetch is used, the other one is also used.
+
+ \deprecated Please rewrite to the async version, that allows the network traffic part of fetching to happen in the background
*/
- SignatureValidationInfo validate(ValidateOptions opt) const;
+ POPPLER_QT6_DEPRECATED SignatureValidationInfo validate(ValidateOptions opt) const;
/**
Validate the signature with @p validationTime as validation time.
@@ -808,8 +833,38 @@ public:
requiring network access, AIAFetch and OCSP,
can be toggled individually. In case of the GPG backend, if either
OCSP is used or AIAFetch is used, the other one is also used.
+
+ \deprecated Please rewrite to the async version, that allows the network traffic part of fetching to happen in the background
+ */
+ POPPLER_QT6_DEPRECATED SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const;
+
+ /**
+ Validate the signature with @p validationTime as validation time.
+
+ Reset signature validatation info of scoped instance.
+
+ \since 24.05
+
+ \note depending on the backend, some options are only
+ partially respected. In case of the NSS backend, the two options
+ requiring network access, AIAFetch and OCSP,
+ can be toggled individually. In case of the GPG backend, if either
+ OCSP is used or AIAFetch is used, the other one is also used.
+
+ \note certificate validation will have started when this function return. See \ref validateResult on how to get certifcate validation
+ \note connections to \ref AsyncObject must happen by the caller
+ before returning control to the event loop, else signals is not guaranteed to be delivered
+ */
+ std::pair<SignatureValidationInfo, std::shared_ptr<AsyncObject>> validateAsync(ValidateOptions opt, const QDateTime &validationTime = {}) const;
+
+ /**
+ * \return the updated signature validation info from validateAsync
+ * \note that this function will block if the result is not yet ready.
+ * Wait for the \ref AsyncObject::done signal to avoid this function blocking on an inconvenient time
+ *
+ * \since 24.05
*/
- SignatureValidationInfo validate(int opt, const QDateTime &validationTime) const;
+ SignatureValidationInfo::CertificateStatus validateResult() const;
/**
* \since 22.02
diff --git a/qt6/tests/check_signature_basics.cpp b/qt6/tests/check_signature_basics.cpp
index e256ea34..b044e81e 100644
--- a/qt6/tests/check_signature_basics.cpp
+++ b/qt6/tests/check_signature_basics.cpp
@@ -110,7 +110,8 @@ void TestSignatureBasics::testSignerInfo()
auto signatureFields = doc->getSignatureFields();
QCOMPARE(signatureFields[0]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature0_B_" });
QCOMPARE(signatureFields[0]->getSignatureType(), ETSI_CAdES_detached);
- auto siginfo0 = signatureFields[0]->validateSignature(false, false, -1 /* now */, false, false);
+ auto siginfo0 = signatureFields[0]->validateSignatureAsync(false, false, -1 /* now */, false, false, {});
+ signatureFields[0]->validateSignatureResult();
#ifdef ENABLE_SIGNATURES
QCOMPARE(siginfo0->getSignerName(), std::string { "Koch, Werner" });
QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Sha256);
@@ -123,7 +124,8 @@ void TestSignatureBasics::testSignerInfo()
QCOMPARE(signatureFields[1]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature1_B_" });
QCOMPARE(signatureFields[1]->getSignatureType(), ETSI_CAdES_detached);
- auto siginfo1 = signatureFields[1]->validateSignature(false, false, -1 /* now */, false, false);
+ auto siginfo1 = signatureFields[1]->validateSignatureAsync(false, false, -1 /* now */, false, false, {});
+ signatureFields[1]->validateSignatureResult();
#ifdef ENABLE_SIGNATURES
QCOMPARE(siginfo1->getSignerName(), std::string { "Koch, Werner" });
QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Sha256);
diff --git a/qt6/tests/poppler-forms.cpp b/qt6/tests/poppler-forms.cpp
index d52bab4b..f1b2d05c 100644
--- a/qt6/tests/poppler-forms.cpp
+++ b/qt6/tests/poppler-forms.cpp
@@ -123,6 +123,8 @@ static std::ostream &operator<<(std::ostream &out, Poppler::SignatureValidationI
case Poppler::SignatureValidationInfo::CertificateNotVerified:
out << "NotVerifiedYet";
break;
+ case Poppler::SignatureValidationInfo::CertificateVerificationInProgress:
+ out << "InProgress";
}
return out;
}
diff --git a/utils/pdfsig.cc b/utils/pdfsig.cc
index fd365ab2..fe588f10 100644
--- a/utils/pdfsig.cc
+++ b/utils/pdfsig.cc
@@ -574,6 +574,16 @@ int main(int argc, char *argv[])
printf("File '%s' does not contain any signatures\n", fileName->c_str());
return 2;
}
+ std::unordered_map<int, SignatureInfo *> signatureInfos;
+ for (unsigned int i = 0; i < sigCount; i++) {
+ // Let's start the signature check first for signatures.
+ // we can always wait for completion later
+ FormFieldSignature *ffs = signatures.at(i);
+ if (ffs->getSignatureType() == unsigned_signature_field) {
+ continue;
+ }
+ signatureInfos[i] = ffs->validateSignatureAsync(!dontVerifyCert, false, -1 /* now */, !noOCSPRevocationCheck, useAIACertFetch, {});
+ }
for (unsigned int i = 0; i < sigCount; i++) {
FormFieldSignature *ffs = signatures.at(i);
@@ -589,7 +599,8 @@ int main(int argc, char *argv[])
continue;
}
- const SignatureInfo *sig_info = ffs->validateSignature(!dontVerifyCert, false, -1 /* now */, !noOCSPRevocationCheck, useAIACertFetch);
+ const SignatureInfo *sig_info = signatureInfos[i];
+ CertificateValidationStatus certificateStatus = ffs->validateSignatureResult();
printf(" - Signer Certificate Common Name: %s\n", sig_info->getSignerName().c_str());
printf(" - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN().c_str());
printf(" - Signing Time: %s\n", time_str = getReadableTime(sig_info->getSigningTime()));
@@ -649,7 +660,7 @@ int main(int argc, char *argv[])
if (sig_info->getSignatureValStatus() != SIGNATURE_VALID || dontVerifyCert) {
continue;
}
- printf(" - Certificate Validation: %s\n", getReadableCertState(sig_info->getCertificateValStatus()));
+ printf(" - Certificate Validation: %s\n", getReadableCertState(certificateStatus));
}
return 0;