[poppler] 2 commits - CMakeLists.txt config.h.cmake poppler/CryptoSignBackend.cc poppler/CryptoSignBackend.h poppler/Form.cc poppler/Form.h poppler/SignatureHandler.cc poppler/SignatureHandler.h qt5/src qt6/src utils/pdfsig.cc

GitLab Mirror gitlab-mirror at kemper.freedesktop.org
Thu Apr 13 08:58:23 UTC 2023


 CMakeLists.txt               |   25 ++++++++++
 config.h.cmake               |    6 ++
 poppler/CryptoSignBackend.cc |   97 +++++++++++++++++++++++++++++++++++++++++
 poppler/CryptoSignBackend.h  |  101 +++++++++++++++++++++++++++++++++++++++++++
 poppler/Form.cc              |   87 ++++++++++++++++++-------------------
 poppler/Form.h               |    8 ++-
 poppler/SignatureHandler.cc  |   56 ++++++++++++-----------
 poppler/SignatureHandler.h   |   45 ++++++++++---------
 qt5/src/poppler-form.cc      |   30 ++++++------
 qt6/src/poppler-form.cc      |   30 ++++++------
 utils/pdfsig.cc              |    3 -
 11 files changed, 366 insertions(+), 122 deletions(-)

New commits:
commit cc5ac1665aa3056d1f90a12e12d24a02536647e0
Author: Sune Vuorela <sune at vuorela.dk>
Date:   Mon Mar 27 10:40:19 2023 +0200

    Adapt signature code to the backend interface
    
    Also set the signature info a bit later;
    gpgme only gives us the info after the validateSignature call

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c6c757c8..63a0970b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -358,6 +358,29 @@ if(TIFF_FOUND)
   set(ENABLE_LIBTIFF ON)
 endif()
 
+set(SIGNATURE_BACKENDS "")
+if(ENABLE_NSS3)
+  list(APPEND SIGNATURE_BACKENDS "NSS")
+endif()
+
+list(LENGTH SIGNATURE_BACKENDS _signing_backends_count)
+if (_signing_backends_count GREATER 0)
+  if (NOT DEFAULT_SIGNATURE_BACKEND)
+    # If not specified at compiletime, we take the first one added.
+    # This means that the order we append them to the list is significant
+    list(GET SIGNATURE_BACKENDS 0 DEFAULT_SIGNATURE_BACKEND)
+  endif()
+  if (NOT DEFAULT_SIGNATURE_BACKEND IN_LIST SIGNATURE_BACKENDS)
+    message(FATAL_ERROR "default signature backend must be one of ${SIGNATURE_BACKENDS}, was ${DEFAULT_SIGNATURE_BACKEND}")
+  endif()
+  set(ENABLE_SIGNATURES ON)
+endif()
+if (NOT DEFAULT_SIGNATURE_BACKEND)
+  set(DEFAULT_SIGNATURE_BACKEND "None")
+endif()
+
+
+
 # Recent versions of poppler-data install a .pc file.
 # Use it to determine the encoding data path, if available.
 # Default to the same prefix otherwise.
@@ -418,6 +441,7 @@ set(poppler_SRCS
   poppler/Catalog.cc
   poppler/CharCodeToUnicode.cc
   poppler/CMap.cc
+  poppler/CryptoSignBackend.cc
   poppler/DateInfo.cc
   poppler/Decrypt.cc
   poppler/Dict.cc
@@ -836,6 +860,7 @@ show_end_message_yesno("use libtiff" ENABLE_LIBTIFF)
 show_end_message_yesno("use zlib compress" ENABLE_ZLIB)
 show_end_message_yesno("use zlib uncompress" ENABLE_ZLIB_UNCOMPRESS)
 show_end_message_yesno("use nss3" ENABLE_NSS3)
+show_end_message("  default signature backend" ${DEFAULT_SIGNATURE_BACKEND})
 show_end_message_yesno("use curl" ENABLE_LIBCURL)
 show_end_message_yesno("use libopenjpeg2" WITH_OPENJPEG)
 show_end_message_yesno("use lcms2" USE_CMS)
diff --git a/config.h.cmake b/config.h.cmake
index ea679a4f..4d6ef551 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -27,6 +27,12 @@
 /* Build against libnss3 for digital signature validation */
 #cmakedefine ENABLE_NSS3 1
 
+/* Signatures enabled */
+#cmakedefine ENABLE_SIGNATURES 1
+
+/* Default signature backend */
+#cmakedefine DEFAULT_SIGNATURE_BACKEND "${DEFAULT_SIGNATURE_BACKEND}"
+
 /* Use cairo for rendering. */
 #cmakedefine HAVE_CAIRO 1
 
diff --git a/poppler/Form.cc b/poppler/Form.cc
index 2c0c35a4..b5a41c53 100644
--- a/poppler/Form.cc
+++ b/poppler/Form.cc
@@ -63,9 +63,7 @@
 #include "Form.h"
 #include "PDFDoc.h"
 #include "DateInfo.h"
-#ifdef ENABLE_NSS3
-#    include "SignatureHandler.h"
-#endif
+#include "CryptoSignBackend.h"
 #include "SignatureInfo.h"
 #include "CertificateInfo.h"
 #include "XRef.h"
@@ -573,10 +571,12 @@ SignatureInfo *FormWidgetSignature::validateSignature(bool doVerifyCert, bool fo
     return static_cast<FormFieldSignature *>(field)->validateSignature(doVerifyCert, forceRevalidation, validationTime, ocspRevocationCheck, enableAIA);
 }
 
-#ifdef ENABLE_NSS3
 // update hash with the specified range of data from the file
-static bool hashFileRange(FILE *f, SignatureSignHandler *handler, Goffset start, Goffset end)
+static bool hashFileRange(FILE *f, CryptoSign::SigningInterface *handler, Goffset start, Goffset end)
 {
+    if (!handler) {
+        return false;
+    }
     const int BUF_SIZE = 65536;
 
     unsigned char *buf = new unsigned char[BUF_SIZE];
@@ -594,27 +594,29 @@ static bool hashFileRange(FILE *f, SignatureSignHandler *handler, Goffset start,
             delete[] buf;
             return false;
         }
-        handler->updateHash(buf, len);
+        handler->addData(buf, len);
         start += len;
     }
     delete[] buf;
     return true;
 }
-#endif
 
 bool FormWidgetSignature::signDocument(const std::string &saveFilename, const std::string &certNickname, const std::string &password, const GooString *reason, const GooString *location, const std::optional<GooString> &ownerPassword,
                                        const std::optional<GooString> &userPassword)
 {
-#ifdef ENABLE_NSS3
+    auto backend = CryptoSign::Factory::createActive();
+    if (!backend) {
+        return false;
+    }
     if (certNickname.empty()) {
         fprintf(stderr, "signDocument: Empty nickname\n");
         return false;
     }
 
-    SignatureSignHandler sigHandler(certNickname, HashAlgorithm::Sha256);
+    auto sigHandler = backend->createSigningHandler(certNickname, HashAlgorithm::Sha256);
 
     FormFieldSignature *signatureField = static_cast<FormFieldSignature *>(field);
-    std::unique_ptr<X509CertificateInfo> certInfo = sigHandler.getCertificateInfo();
+    std::unique_ptr<X509CertificateInfo> certInfo = sigHandler->getCertificateInfo();
     if (!certInfo) {
         fprintf(stderr, "signDocument: error getting signature info\n");
         return false;
@@ -625,7 +627,7 @@ bool FormWidgetSignature::signDocument(const std::string &saveFilename, const st
 
     Object vObj(new Dict(xref));
     Ref vref = xref->addIndirectObject(vObj);
-    if (!createSignature(vObj, vref, GooString(signerName), maxSupportedSignatureSize, reason, location)) {
+    if (!createSignature(vObj, vref, GooString(signerName), CryptoSign::maxSupportedSignatureSize, reason, location)) {
         return false;
     }
 
@@ -653,33 +655,33 @@ bool FormWidgetSignature::signDocument(const std::string &saveFilename, const st
     }
 
     // compute hash of byte ranges
-    if (!hashFileRange(file, &sigHandler, 0LL, sigStart)) {
+    if (!hashFileRange(file, sigHandler.get(), 0LL, sigStart)) {
         fclose(file);
         return false;
     }
-    if (!hashFileRange(file, &sigHandler, sigEnd, fileSize)) {
+    if (!hashFileRange(file, sigHandler.get(), sigEnd, fileSize)) {
         fclose(file);
         return false;
     }
 
     // and sign it
-    const std::unique_ptr<GooString> signature = sigHandler.signDetached(password);
+    auto signature = sigHandler->signDetached(password);
     if (!signature) {
         fclose(file);
         return false;
     }
 
-    if (signature->getLength() > maxSupportedSignatureSize) {
+    if (signature->getLength() > CryptoSign::maxSupportedSignatureSize) {
         fclose(file);
         return false;
     }
 
     // pad with zeroes to placeholder length
     auto length = signature->getLength();
-    signature->append(std::string(maxSupportedSignatureSize - length, '\0'));
+    signature->append(std::string(CryptoSign::maxSupportedSignatureSize - length, '\0'));
 
     // write signature to saved file
-    if (!updateSignature(file, sigStart, sigEnd, signature.get())) {
+    if (!updateSignature(file, sigStart, sigEnd, signature.value())) {
         fprintf(stderr, "signDocument: unable update signature\n");
         fclose(file);
         return false;
@@ -689,9 +691,6 @@ bool FormWidgetSignature::signDocument(const std::string &saveFilename, const st
     fclose(file);
 
     return true;
-#else
-    return false;
-#endif
 }
 
 bool FormWidgetSignature::signDocumentWithAppearance(const std::string &saveFilename, const std::string &certNickname, const std::string &password, const GooString *reason, const GooString *location,
@@ -859,18 +858,18 @@ bool FormWidgetSignature::updateOffsets(FILE *f, Goffset objStart, Goffset objEn
 }
 
 // Overwrite signature string in the file with new signature
-bool FormWidgetSignature::updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString *signature)
+bool FormWidgetSignature::updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString &signature)
 {
-    if (signature->getLength() * 2 + 2 != sigEnd - sigStart) {
+    if (signature.getLength() * 2 + 2 != sigEnd - sigStart) {
         return false;
     }
 
     if (Gfseek(f, sigStart, SEEK_SET) != 0) {
         return false;
     }
-    const char *c = signature->c_str();
+    const char *c = signature.c_str();
     fprintf(f, "<");
-    for (int i = 0; i < signature->getLength(); i++) {
+    for (int i = 0; i < signature.getLength(); i++) {
         unsigned char value = *(c + i) & 0x000000ff;
         fprintf(f, "%2.2x", value);
     }
@@ -2305,9 +2304,11 @@ void FormFieldSignature::parseInfo()
     }
 }
 
-void FormFieldSignature::hashSignedDataBlock(SignatureVerificationHandler *handler, Goffset block_len)
+void FormFieldSignature::hashSignedDataBlock(CryptoSign::VerificationInterface *handler, Goffset block_len)
 {
-#ifdef ENABLE_NSS3
+    if (!handler) {
+        return;
+    }
     const int BLOCK_SIZE = 4096;
     unsigned char signed_data_buffer[BLOCK_SIZE];
 
@@ -2316,15 +2317,14 @@ void FormFieldSignature::hashSignedDataBlock(SignatureVerificationHandler *handl
         Goffset bytes_left = block_len - i;
         if (bytes_left < BLOCK_SIZE) {
             doc->getBaseStream()->doGetChars(static_cast<int>(bytes_left), signed_data_buffer);
-            handler->updateHash(signed_data_buffer, static_cast<int>(bytes_left));
+            handler->addData(signed_data_buffer, static_cast<int>(bytes_left));
             i = block_len;
         } else {
             doc->getBaseStream()->doGetChars(BLOCK_SIZE, signed_data_buffer);
-            handler->updateHash(signed_data_buffer, BLOCK_SIZE);
+            handler->addData(signed_data_buffer, BLOCK_SIZE);
             i += BLOCK_SIZE;
         }
     }
-#endif
 }
 
 FormSignatureType FormWidgetSignature::signatureType() const
@@ -2339,7 +2339,11 @@ void FormWidgetSignature::setSignatureType(FormSignatureType fst)
 
 SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool forceRevalidation, time_t validationTime, bool ocspRevocationCheck, bool enableAIA)
 {
-#ifdef ENABLE_NSS3
+    auto backend = CryptoSign::Factory::createActive();
+    if (!backend) {
+        return signature_info;
+    }
+
     if (signature_info->getSignatureValStatus() != SIGNATURE_NOT_VERIFIED && !forceRevalidation) {
         return signature_info;
     }
@@ -2363,7 +2367,7 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
     const int signature_len = signature->getLength();
     std::vector<unsigned char> signatureData(signature_len);
     memcpy(signatureData.data(), signature->c_str(), signature_len);
-    SignatureVerificationHandler signature_handler(std::move(signatureData));
+    auto signature_handler = backend->createVerificationHandler(std::move(signatureData));
 
     Goffset fileLength = doc->getBaseStream()->getLength();
     for (int i = 0; i < arrayLen / 2; i++) {
@@ -2384,35 +2388,32 @@ SignatureInfo *FormFieldSignature::validateSignature(bool doVerifyCert, bool for
         }
 
         doc->getBaseStream()->setPos(offset);
-        hashSignedDataBlock(&signature_handler, len);
+        hashSignedDataBlock(signature_handler.get(), len);
     }
 
-    signature_info->setSignerName(signature_handler.getSignerName().c_str());
-    signature_info->setSubjectDN(signature_handler.getSignerSubjectDN());
-    signature_info->setHashAlgorithm(signature_handler.getHashAlgorithm());
-
     if (!signature_info->isSubfilterSupported()) {
         error(errUnimplemented, 0, "Unable to validate this type of signature");
         return signature_info;
     }
-
-    const SignatureValidationStatus sig_val_state = signature_handler.validateSignature();
+    const SignatureValidationStatus sig_val_state = signature_handler->validateSignature();
     signature_info->setSignatureValStatus(sig_val_state);
+    signature_info->setSignerName(signature_handler->getSignerName());
+    signature_info->setSubjectDN(signature_handler->getSignerSubjectDN());
+    signature_info->setHashAlgorithm(signature_handler->getHashAlgorithm());
 
     // verify if signature contains a 'signing time' attribute
-    if (signature_handler.getSigningTime() != 0) {
-        signature_info->setSigningTime(signature_handler.getSigningTime());
+    if (signature_handler->getSigningTime() != std::chrono::system_clock::time_point {}) {
+        signature_info->setSigningTime(std::chrono::system_clock::to_time_t(signature_handler->getSigningTime()));
     }
 
     if (sig_val_state != SIGNATURE_VALID || !doVerifyCert) {
         return signature_info;
     }
 
-    const CertificateValidationStatus cert_val_state = signature_handler.validateCertificate(validationTime, ocspRevocationCheck, enableAIA);
+    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_info->setCertificateInfo(signature_handler.getCertificateInfo());
+    signature_info->setCertificateInfo(signature_handler->getCertificateInfo());
 
-#endif
     return signature_info;
 }
 
diff --git a/poppler/Form.h b/poppler/Form.h
index 80be38e6..5ebdec0e 100644
--- a/poppler/Form.h
+++ b/poppler/Form.h
@@ -56,7 +56,9 @@ class GfxResources;
 class PDFDoc;
 class SignatureInfo;
 class X509CertificateInfo;
-class SignatureVerificationHandler;
+namespace CryptoSign {
+class VerificationInterface;
+}
 
 enum FormFieldType
 {
@@ -328,7 +330,7 @@ private:
     bool getObjectStartEnd(const GooString &filename, int objNum, Goffset *objStart, Goffset *objEnd, const std::optional<GooString> &ownerPassword, const std::optional<GooString> &userPassword);
     bool updateOffsets(FILE *f, Goffset objStart, Goffset objEnd, Goffset *sigStart, Goffset *sigEnd, Goffset *fileSize);
 
-    bool updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString *signature);
+    bool updateSignature(FILE *f, Goffset sigStart, Goffset sigEnd, const GooString &signature);
 };
 
 //------------------------------------------------------------------------
@@ -640,7 +642,7 @@ public:
 
 private:
     void parseInfo();
-    void hashSignedDataBlock(SignatureVerificationHandler *handler, Goffset block_len);
+    void hashSignedDataBlock(CryptoSign::VerificationInterface *handler, Goffset block_len);
 
     FormSignatureType signature_type;
     Object byte_range;
diff --git a/poppler/SignatureHandler.cc b/poppler/SignatureHandler.cc
index 99163000..a306c358 100644
--- a/poppler/SignatureHandler.cc
+++ b/poppler/SignatureHandler.cc
@@ -534,7 +534,7 @@ std::string SignatureVerificationHandler::getSignerSubjectDN() const
     return std::string { signing_cert->subjectName };
 }
 
-time_t SignatureVerificationHandler::getSigningTime() const
+std::chrono::system_clock::time_point SignatureVerificationHandler::getSigningTime() const
 {
     if (!CMSSignerInfo) {
         return {};
@@ -542,10 +542,10 @@ time_t SignatureVerificationHandler::getSigningTime() const
     PRTime sTime; // time in microseconds since the epoch
 
     if (NSS_CMSSignerInfo_GetSigningTime(CMSSignerInfo, &sTime) != SECSuccess) {
-        return 0;
+        return {};
     }
 
-    return static_cast<time_t>(sTime / 1000000);
+    return std::chrono::system_clock::from_time_t(static_cast<time_t>(sTime / 1000000));
 }
 
 static X509CertificateInfo::EntityInfo getEntityInfo(CERTName *entityName)
@@ -787,14 +787,14 @@ HashAlgorithm SignatureVerificationHandler::getHashAlgorithm() const
     }
 }
 
-void SignatureVerificationHandler::updateHash(unsigned char *data_block, int data_len)
+void SignatureVerificationHandler::addData(unsigned char *data_block, int data_len)
 {
     if (hashContext) {
         hashContext->updateHash(data_block, data_len);
     }
 }
 
-void SignatureSignHandler::updateHash(unsigned char *data_block, int data_len)
+void SignatureSignHandler::addData(unsigned char *data_block, int data_len)
 {
     hashContext->updateHash(data_block, data_len);
 }
@@ -947,7 +947,7 @@ SignatureValidationStatus SignatureVerificationHandler::validateSignature()
     }
 }
 
-CertificateValidationStatus SignatureVerificationHandler::validateCertificate(time_t validation_time, bool ocspRevocationCheck, bool useAIACertFetch)
+CertificateValidationStatus SignatureVerificationHandler::validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch)
 {
     CERTCertificate *cert;
 
@@ -960,8 +960,8 @@ CertificateValidationStatus SignatureVerificationHandler::validateCertificate(ti
     }
 
     PRTime vTime = 0; // time in microseconds since the epoch, special value 0 means now
-    if (validation_time > 0) {
-        vTime = 1000000 * (PRTime)validation_time;
+    if (validation_time > std::chrono::system_clock::time_point {}) {
+        vTime = 1000000 * (PRTime)std::chrono::system_clock::to_time_t(validation_time);
     }
     CERTValInParam inParams[4];
     inParams[0].type = cert_pi_revocationFlags;
@@ -1003,10 +1003,10 @@ CertificateValidationStatus SignatureVerificationHandler::validateCertificate(ti
     return CERTIFICATE_GENERIC_ERROR;
 }
 
-std::unique_ptr<GooString> SignatureSignHandler::signDetached(const std::string &password)
+std::optional<GooString> SignatureSignHandler::signDetached(const std::string &password)
 {
     if (!hashContext) {
-        return nullptr;
+        return {};
     }
     std::vector<unsigned char> digest_buffer = hashContext->endHash();
     SECItem digest;
@@ -1022,54 +1022,54 @@ std::unique_ptr<GooString> SignatureSignHandler::signDetached(const std::string
     };
     std::unique_ptr<NSSCMSMessage, NSSCMSMessageDestroyer> cms_msg { NSS_CMSMessage_Create(nullptr) };
     if (!cms_msg) {
-        return nullptr;
+        return {};
     }
 
     NSSCMSSignedData *cms_sd = NSS_CMSSignedData_Create(cms_msg.get());
     if (!cms_sd) {
-        return nullptr;
+        return {};
     }
 
     NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(cms_msg.get());
 
     if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg.get(), cms_cinfo, cms_sd) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     cms_cinfo = NSS_CMSSignedData_GetContentInfo(cms_sd);
 
     // Attach NULL data as detached data
     if (NSS_CMSContentInfo_SetContent_Data(cms_msg.get(), cms_cinfo, nullptr, PR_TRUE) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     // hardcode SHA256 these days...
     NSSCMSSignerInfo *cms_signer = NSS_CMSSignerInfo_Create(cms_msg.get(), signing_cert, SEC_OID_SHA256);
     if (!cms_signer) {
-        return nullptr;
+        return {};
     }
 
     if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     if (NSS_CMSSignedData_AddCertificate(cms_sd, signing_cert) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     if (NSS_CMSSignedData_AddSignerInfo(cms_sd, cms_signer) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     if (NSS_CMSSignedData_SetDigestValue(cms_sd, SEC_OID_SHA256, &digest) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     struct PLArenaFreeFalse
     {
         void operator()(PLArenaPool *arena) { PORT_FreeArena(arena, PR_FALSE); }
     };
-    std::unique_ptr<PLArenaPool, PLArenaFreeFalse> arena { PORT_NewArena(maxSupportedSignatureSize) };
+    std::unique_ptr<PLArenaPool, PLArenaFreeFalse> arena { PORT_NewArena(CryptoSign::maxSupportedSignatureSize) };
 
     // Add the signing certificate as a signed attribute.
     ESSCertIDv2 *aCertIDs[2];
@@ -1084,7 +1084,7 @@ std::unique_ptr<GooString> SignatureSignHandler::signDetached(const std::string
     unsigned char certhash[32];
     SECStatus rv = PK11_HashBuf(SEC_OID_SHA256, certhash, signing_cert->derCert.data, signing_cert->derCert.len);
     if (rv != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     aCertHashItem.type = siBuffer;
@@ -1107,7 +1107,7 @@ std::unique_ptr<GooString> SignatureSignHandler::signDetached(const std::string
 
     SECItem *pEncodedCertificate = SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate, SigningCertificateV2Template);
     if (!pEncodedCertificate) {
-        return nullptr;
+        return {};
     }
 
     NSSCMSAttribute aAttribute;
@@ -1129,7 +1129,7 @@ std::unique_ptr<GooString> SignatureSignHandler::signDetached(const std::string
      *   smime(16) id-aa(2) 47 }
      */
     if (my_SEC_StringToOID(arena.get(), &aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     aOidData.offset = SEC_OID_UNKNOWN;
@@ -1141,7 +1141,7 @@ std::unique_ptr<GooString> SignatureSignHandler::signDetached(const std::string
     aAttribute.encoded = PR_TRUE;
 
     if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer, &aAttribute) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
     SECItem cms_output;
@@ -1150,14 +1150,14 @@ std::unique_ptr<GooString> SignatureSignHandler::signDetached(const std::string
 
     NSSCMSEncoderContext *cms_ecx = NSS_CMSEncoder_Start(cms_msg.get(), nullptr, nullptr, &cms_output, arena.get(), passwordCallback, password.empty() ? nullptr : const_cast<char *>(password.c_str()), nullptr, nullptr, nullptr, nullptr);
     if (!cms_ecx) {
-        return nullptr;
+        return {};
     }
 
     if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess) {
-        return nullptr;
+        return {};
     }
 
-    auto signature = std::make_unique<GooString>(reinterpret_cast<const char *>(cms_output.data), cms_output.len);
+    auto signature = GooString(reinterpret_cast<const char *>(cms_output.data), cms_output.len);
 
     SECITEM_FreeItem(pEncodedCertificate, PR_TRUE);
 
@@ -1238,3 +1238,5 @@ HashAlgorithm HashContext::getHashAlgorithm() const
 {
     return digest_alg_tag;
 }
+
+NSSCryptoSignBackend::~NSSCryptoSignBackend() = default;
diff --git a/poppler/SignatureHandler.h b/poppler/SignatureHandler.h
index c9fb575e..8a978f09 100644
--- a/poppler/SignatureHandler.h
+++ b/poppler/SignatureHandler.h
@@ -43,11 +43,7 @@
 #include <secoid.h>
 #include <secmodt.h>
 #include <sechash.h>
-
-// experiments seems to say that this is a bit above
-// what we have seen in the wild, and much larger than
-// what we have managed to get nss and gpgme to create.
-static const int maxSupportedSignatureSize = 10000;
+#include "CryptoSignBackend.h"
 
 class HashContext
 {
@@ -67,20 +63,20 @@ private:
     HashAlgorithm digest_alg_tag;
 };
 
-class POPPLER_PRIVATE_EXPORT SignatureVerificationHandler
+class POPPLER_PRIVATE_EXPORT SignatureVerificationHandler final : public CryptoSign::VerificationInterface
 {
 public:
     explicit SignatureVerificationHandler(std::vector<unsigned char> &&p7data);
-    ~SignatureVerificationHandler();
-    SignatureValidationStatus validateSignature();
-    time_t getSigningTime() const;
-    std::string getSignerName() const;
-    std::string getSignerSubjectDN() const;
+    ~SignatureVerificationHandler() final;
+    SignatureValidationStatus validateSignature() final;
+    std::chrono::system_clock::time_point getSigningTime() const final;
+    std::string getSignerName() const final;
+    std::string getSignerSubjectDN() const final;
     // Use -1 as validation_time for now
-    CertificateValidationStatus validateCertificate(time_t validation_time, bool ocspRevocationCheck, bool useAIACertFetch);
-    std::unique_ptr<X509CertificateInfo> getCertificateInfo() const;
-    void updateHash(unsigned char *data_block, int data_len);
-    HashAlgorithm getHashAlgorithm() const;
+    CertificateValidationStatus validateCertificate(std::chrono::system_clock::time_point validation_time, bool ocspRevocationCheck, bool useAIACertFetch) final;
+    std::unique_ptr<X509CertificateInfo> getCertificateInfo() const final;
+    void addData(unsigned char *data_block, int data_len) final;
+    HashAlgorithm getHashAlgorithm() const final;
 
     SignatureVerificationHandler(const SignatureVerificationHandler &) = delete;
     SignatureVerificationHandler &operator=(const SignatureVerificationHandler &) = delete;
@@ -94,14 +90,14 @@ private:
     std::unique_ptr<HashContext> hashContext;
 };
 
-class POPPLER_PRIVATE_EXPORT SignatureSignHandler
+class POPPLER_PRIVATE_EXPORT SignatureSignHandler final : public CryptoSign::SigningInterface
 {
 public:
     SignatureSignHandler(const std::string &certNickname, HashAlgorithm digestAlgTag);
-    ~SignatureSignHandler();
-    std::unique_ptr<X509CertificateInfo> getCertificateInfo() const;
-    void updateHash(unsigned char *data_block, int data_len);
-    std::unique_ptr<GooString> signDetached(const std::string &password);
+    ~SignatureSignHandler() final;
+    std::unique_ptr<X509CertificateInfo> getCertificateInfo() const final;
+    void addData(unsigned char *data_block, int data_len) final;
+    std::optional<GooString> signDetached(const std::string &password) final;
 
     SignatureSignHandler(const SignatureSignHandler &) = delete;
     SignatureSignHandler &operator=(const SignatureSignHandler &) = delete;
@@ -133,4 +129,13 @@ private:
     static std::string sNssDir;
 };
 
+class NSSCryptoSignBackend final : public CryptoSign::Backend
+{
+public:
+    std::unique_ptr<CryptoSign::VerificationInterface> createVerificationHandler(std::vector<unsigned char> &&pkcs7) final { return std::make_unique<SignatureVerificationHandler>(std::move(pkcs7)); }
+    std::unique_ptr<CryptoSign::SigningInterface> createSigningHandler(const std::string &certID, HashAlgorithm digestAlgTag) final { return std::make_unique<SignatureSignHandler>(certID, digestAlgTag); }
+    std::vector<std::unique_ptr<X509CertificateInfo>> getAvailableSigningCertificates() final { return SignatureHandler::getAvailableSigningCertificates(); }
+    ~NSSCryptoSignBackend() final;
+};
+
 #endif
diff --git a/qt5/src/poppler-form.cc b/qt5/src/poppler-form.cc
index dfdcd391..05852bb4 100644
--- a/qt5/src/poppler-form.cc
+++ b/qt5/src/poppler-form.cc
@@ -45,6 +45,7 @@
 #include <Link.h>
 #include <SignatureInfo.h>
 #include <CertificateInfo.h>
+#include <CryptoSignBackend.h>
 #ifdef ENABLE_NSS3
 #    include <SignatureHandler.h>
 #endif
@@ -56,10 +57,6 @@
 #include <cmath>
 #include <cctype>
 
-#ifdef ENABLE_NSS3
-#    include <hasht.h>
-#endif
-
 namespace {
 
 Qt::Alignment formTextAlignment(::FormWidget *fm)
@@ -779,14 +776,18 @@ QByteArray CertificateInfo::certificateData() const
 
 bool CertificateInfo::checkPassword(const QString &password) const
 {
-#ifdef ENABLE_NSS3
+#ifdef ENABLE_SIGNATURES
+    auto backend = CryptoSign::Factory::createActive();
+    if (!backend) {
+        return false;
+    }
     Q_D(const CertificateInfo);
-    SignatureSignHandler sigHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256);
+    auto sigHandler = backend->createSigningHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256);
     unsigned char buffer[5];
     memcpy(buffer, "test", 5);
-    sigHandler.updateHash(buffer, 5);
-    std::unique_ptr<GooString> tmpSignature = sigHandler.signDetached(password.toStdString());
-    return tmpSignature.get() != nullptr;
+    sigHandler->addData(buffer, 5);
+    std::optional<GooString> tmpSignature = sigHandler->signDetached(password.toStdString());
+    return tmpSignature.has_value();
 #else
     return false;
 #endif
@@ -856,7 +857,7 @@ QString SignatureValidationInfo::reason() const
 
 SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const
 {
-#ifdef ENABLE_NSS3
+#ifdef ENABLE_SIGNATURES
     Q_D(const SignatureValidationInfo);
 
     switch (d->hash_algorithm) {
@@ -1131,16 +1132,17 @@ bool hasNSSSupport()
 
 QVector<CertificateInfo> getAvailableSigningCertificates()
 {
+    auto backend = CryptoSign::Factory::createActive();
+    if (!backend) {
+        return {};
+    }
     QVector<CertificateInfo> vReturnCerts;
-
-#ifdef ENABLE_NSS3
-    std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = SignatureHandler::getAvailableSigningCertificates();
+    std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = backend->getAvailableSigningCertificates();
 
     for (auto &cert : vCerts) {
         CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(cert.get());
         vReturnCerts.append(CertificateInfo(certPriv));
     }
-#endif
 
     return vReturnCerts;
 }
diff --git a/qt6/src/poppler-form.cc b/qt6/src/poppler-form.cc
index b415c08e..83dc79d7 100644
--- a/qt6/src/poppler-form.cc
+++ b/qt6/src/poppler-form.cc
@@ -45,6 +45,7 @@
 #include <Link.h>
 #include <SignatureInfo.h>
 #include <CertificateInfo.h>
+#include <CryptoSignBackend.h>
 #ifdef ENABLE_NSS3
 #    include <SignatureHandler.h>
 #endif
@@ -56,10 +57,6 @@
 #include <cmath>
 #include <cctype>
 
-#ifdef ENABLE_NSS3
-#    include <hasht.h>
-#endif
-
 namespace {
 
 Qt::Alignment formTextAlignment(::FormWidget *fm)
@@ -779,14 +776,18 @@ QByteArray CertificateInfo::certificateData() const
 
 bool CertificateInfo::checkPassword(const QString &password) const
 {
-#ifdef ENABLE_NSS3
+#ifdef ENABLE_SIGNATURES
+    auto backend = CryptoSign::Factory::createActive();
+    if (!backend) {
+        return false;
+    }
     Q_D(const CertificateInfo);
-    SignatureSignHandler sigHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256);
+    auto sigHandler = backend->createSigningHandler(d->nick_name.toStdString(), HashAlgorithm::Sha256);
     unsigned char buffer[5];
     memcpy(buffer, "test", 5);
-    sigHandler.updateHash(buffer, 5);
-    std::unique_ptr<GooString> tmpSignature = sigHandler.signDetached(password.toStdString());
-    return tmpSignature.get() != nullptr;
+    sigHandler->addData(buffer, 5);
+    std::optional<GooString> tmpSignature = sigHandler->signDetached(password.toStdString());
+    return tmpSignature.has_value();
 #else
     return false;
 #endif
@@ -856,7 +857,7 @@ QString SignatureValidationInfo::reason() const
 
 SignatureValidationInfo::HashAlgorithm SignatureValidationInfo::hashAlgorithm() const
 {
-#ifdef ENABLE_NSS3
+#ifdef ENABLE_SIGNATURES
     Q_D(const SignatureValidationInfo);
 
     switch (d->hash_algorithm) {
@@ -1131,16 +1132,17 @@ bool hasNSSSupport()
 
 QVector<CertificateInfo> getAvailableSigningCertificates()
 {
+    auto backend = CryptoSign::Factory::createActive();
+    if (!backend) {
+        return {};
+    }
     QVector<CertificateInfo> vReturnCerts;
-
-#ifdef ENABLE_NSS3
-    std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = SignatureHandler::getAvailableSigningCertificates();
+    std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = backend->getAvailableSigningCertificates();
 
     for (auto &cert : vCerts) {
         CertificateInfoPrivate *certPriv = createCertificateInfoPrivate(cert.get());
         vReturnCerts.append(CertificateInfo(certPriv));
     }
-#endif
 
     return vReturnCerts;
 }
diff --git a/utils/pdfsig.cc b/utils/pdfsig.cc
index 490795f9..334c9372 100644
--- a/utils/pdfsig.cc
+++ b/utils/pdfsig.cc
@@ -42,6 +42,7 @@
 #include "Error.h"
 #include "GlobalParams.h"
 #include "SignatureHandler.h"
+#include "CryptoSignBackend.h"
 #include "SignatureInfo.h"
 #include "Win32Console.h"
 #include "numberofcharacters.h"
@@ -201,7 +202,7 @@ static std::vector<std::unique_ptr<X509CertificateInfo>> getAvailableSigningCert
         }
     };
     SignatureHandler::setNSSPasswordCallback(passwordCallback);
-    std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = SignatureHandler::getAvailableSigningCertificates();
+    std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = CryptoSign::Factory::createActive()->getAvailableSigningCertificates();
     SignatureHandler::setNSSPasswordCallback({});
     if (passwordNeeded) {
         *error = true;
commit cb96047d029e2145191d79b7ece17b4a215794cd
Author: Sune Vuorela <sune at vuorela.dk>
Date:   Thu Mar 23 16:40:59 2023 +0100

    Backending infrastructure

diff --git a/poppler/CryptoSignBackend.cc b/poppler/CryptoSignBackend.cc
new file mode 100644
index 00000000..86703470
--- /dev/null
+++ b/poppler/CryptoSignBackend.cc
@@ -0,0 +1,97 @@
+//========================================================================
+//
+// CryptoSignBackend.cc
+//
+// This file is licensed under the GPLv2 or later
+//
+// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune at vuorela.dk>
+//========================================================================
+#include "CryptoSignBackend.h"
+#include "config.h"
+#ifdef ENABLE_NSS3
+#    include "SignatureHandler.h"
+#endif
+
+namespace CryptoSign {
+
+void Factory::setPreferredBackend(CryptoSign::Backend::Type backend)
+{
+    preferredBackend = backend;
+}
+static std::string_view toStringView(const char *str)
+{
+    if (str) {
+        return std::string_view(str);
+    }
+    return {};
+}
+
+std::optional<CryptoSign::Backend::Type> Factory::typeFromString(std::string_view string)
+{
+    if (string.empty()) {
+        return std::nullopt;
+    }
+    if ("NSS" == string) {
+        return Backend::Type::NSS3;
+    }
+    return std::nullopt;
+}
+
+CryptoSign::Backend::Type Factory::getActive()
+{
+    if (preferredBackend) {
+        return *preferredBackend;
+    }
+    static auto backendFromEnvironment = typeFromString(toStringView(getenv("POPPLER_SIGNATURE_BACKEND")));
+    if (backendFromEnvironment) {
+        return *backendFromEnvironment;
+    }
+    static auto backendFromCompiledDefault = typeFromString(toStringView(DEFAULT_SIGNATURE_BACKEND));
+    if (backendFromCompiledDefault) {
+        return *backendFromCompiledDefault;
+    }
+
+    return Backend::Type::None;
+}
+static std::vector<Backend::Type> createAvailableBackends()
+{
+    std::vector<Backend::Type> backends;
+#ifdef ENABLE_NSS3
+    backends.push_back(Backend::Type::NSS3);
+#endif
+    return backends;
+}
+std::vector<Backend::Type> Factory::getAvailable()
+{
+    static std::vector<Backend::Type> availableBackends = createAvailableBackends();
+    return availableBackends;
+}
+std::unique_ptr<Backend> Factory::createActive()
+{
+    return create(getActive());
+}
+std::unique_ptr<CryptoSign::Backend> CryptoSign::Factory::create(Backend::Type backend)
+{
+    switch (backend) {
+    case Backend::Type::NSS3:
+#ifdef ENABLE_NSS3
+        return std::make_unique<NSSCryptoSignBackend>();
+#else
+        return nullptr;
+#endif
+    case Backend::Type::None: {
+        return nullptr;
+    }
+    }
+    return nullptr;
+}
+/// backend specific settings
+
+// Android build wants some methods out of line in the interfaces
+Backend::~Backend() = default;
+SigningInterface::~SigningInterface() = default;
+VerificationInterface::~VerificationInterface() = default;
+
+std::optional<Backend::Type> Factory::preferredBackend = std::nullopt;
+
+} // namespace Signature;
diff --git a/poppler/CryptoSignBackend.h b/poppler/CryptoSignBackend.h
new file mode 100644
index 00000000..b106ec47
--- /dev/null
+++ b/poppler/CryptoSignBackend.h
@@ -0,0 +1,101 @@
+//========================================================================
+//
+// CryptoSignBackend.h
+//
+// This file is licensed under the GPLv2 or later
+//
+// Copyright 2023 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune at vuorela.dk>
+//========================================================================
+
+#ifndef SIGNATUREBACKEND_H
+#define SIGNATUREBACKEND_H
+
+#include <vector>
+#include <memory>
+#include <chrono>
+#include <optional>
+#include "HashAlgorithm.h"
+#include "CertificateInfo.h"
+#include "SignatureInfo.h"
+#include "goo/GooString.h"
+#include "poppler_private_export.h"
+
+namespace CryptoSign {
+
+// experiments seems to say that this is a bit above
+// what we have seen in the wild, and much larger than
+// what we have managed to get nss and gpgme to create.
+static const int maxSupportedSignatureSize = 10000;
+
+// Classes to help manage signature backends
+
+class VerificationInterface
+{
+public:
+    virtual void addData(unsigned char *data_block, int data_len) = 0;
+    virtual SignatureValidationStatus validateSignature() = 0;
+    virtual std::chrono::system_clock::time_point getSigningTime() const = 0;
+    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;
+    virtual std::unique_ptr<X509CertificateInfo> getCertificateInfo() const = 0;
+    virtual ~VerificationInterface();
+    VerificationInterface() = default;
+    VerificationInterface(const VerificationInterface &other) = delete;
+    VerificationInterface &operator=(const VerificationInterface &other) = delete;
+};
+
+class SigningInterface
+{
+public:
+    virtual void addData(unsigned char *data_block, int data_len) = 0;
+    virtual std::unique_ptr<X509CertificateInfo> getCertificateInfo() const = 0;
+    virtual std::optional<GooString> signDetached(const std::string &password) = 0;
+    virtual ~SigningInterface();
+    SigningInterface() = default;
+    SigningInterface(const SigningInterface &other) = delete;
+    SigningInterface &operator=(const SigningInterface &other) = delete;
+};
+
+class Backend
+{
+public:
+    enum class Type
+    {
+        None,
+        NSS3
+    };
+    virtual std::unique_ptr<VerificationInterface> createVerificationHandler(std::vector<unsigned char> &&pkcs7) = 0;
+    virtual std::unique_ptr<SigningInterface> createSigningHandler(const std::string &certID, HashAlgorithm digestAlgTag) = 0;
+    virtual std::vector<std::unique_ptr<X509CertificateInfo>> getAvailableSigningCertificates() = 0;
+    virtual ~Backend();
+    Backend() = default;
+    Backend(const Backend &other) = delete;
+    Backend &operator=(const Backend &other) = delete;
+};
+
+class POPPLER_PRIVATE_EXPORT Factory
+{
+public:
+    // Sets the user preferred backend
+    static void setPreferredBackend(Backend::Type backend);
+    // Gets the current active backend
+    // prioritized from 1) setPreferredBackend,
+    //                  2) POPPLER_SIGNATURE_BACKEND
+    //                  3) Compiled in default
+    static Backend::Type getActive();
+    static std::vector<Backend::Type> getAvailable();
+    static std::unique_ptr<Backend> createActive();
+    static std::unique_ptr<Backend> create(Backend::Type);
+    static std::optional<Backend::Type> typeFromString(std::string_view string);
+    Factory() = delete;
+    /// backend specific settings
+
+private:
+    static std::optional<Backend::Type> preferredBackend;
+};
+
+}
+
+#endif // SIGNATUREBACKEND_H


More information about the poppler mailing list