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 <[email protected]> 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 <[email protected]> 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 <[email protected]> +//======================================================================== +#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 <[email protected]> +//======================================================================== + +#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
