blob: d15c79d7e7a4b5e75c297aee07c0989801cb5b77 [file]
//========================================================================
//
// pdfsig.cc
//
// This file is licensed under the GPLv2 or later
//
// Copyright 2015 André Guerreiro <aguerreiro1985@gmail.com>
// Copyright 2015 André Esser <bepandre@hotmail.com>
// Copyright 2015, 2017-2026 Albert Astals Cid <aacid@kde.org>
// Copyright 2016 Markus Kilås <digital@markuspage.com>
// Copyright 2017, 2019 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
// Copyright 2017, 2019 Adrian Johnson <ajohnson@redneon.com>
// Copyright 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@protonmail.com>
// Copyright 2019 Alexey Pavlov <alexpux@gmail.com>
// Copyright 2019. 2023, 2024 Oliver Sander <oliver.sander@tu-dresden.de>
// Copyright 2019 Nelson Efrain A. Cruz <neac03@gmail.com>
// Copyright 2021 Georgiy Sgibnev <georgiy@sgibnev.com>. Work sponsored by lab50.net.
// Copyright 2021 Theofilos Intzoglou <int.teo@gmail.com>
// Copyright 2022 Felix Jung <fxjung@posteo.de>
// Copyright 2022, 2024 Erich E. Hoover <erich.e.hoover@gmail.com>
// Copyright 2023-2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
// Copyright 2025 Blair Bonnett <blair.bonnett@gmail.com>
//
//========================================================================
#include "config.h"
#include <poppler-config.h>
#include <cstdio>
#include <cstdlib>
#include <cstddef>
#include <cstring>
#include <ctime>
#include <fstream>
#include <random>
#include <filesystem>
#include <iomanip>
#include <sstream>
#include "parseargs.h"
#include "goo/gbasename.h"
#include "Form.h"
#include "Page.h"
#include "PDFDoc.h"
#include "PDFDocFactory.h"
#include "DateInfo.h"
#include "GlobalParams.h"
#if ENABLE_NSS3
# include "NSSCryptoSignBackend.h"
#endif
#if ENABLE_GPGME
# include "GPGMECryptoSignBackendConfiguration.h"
#endif
#include "CryptoSignBackend.h"
#include "SignatureInfo.h"
#include "Win32Console.h"
#include "UTF.h"
#if __has_include(<libgen.h>)
# include <libgen.h>
#endif
#ifdef HAVE_GETTEXT
# include <libintl.h>
# include <clocale>
# define _(STRING) gettext(STRING)
#else
# define _(STRING) STRING
#endif
static const char *getReadableSigState(SignatureValidationStatus sig_vs)
{
switch (sig_vs) {
case SIGNATURE_VALID:
return "Signature is Valid.";
case SIGNATURE_INVALID:
return "Signature is Invalid.";
case SIGNATURE_DIGEST_MISMATCH:
return "Digest Mismatch.";
case SIGNATURE_DECODING_ERROR:
return "Document isn't signed or corrupted data.";
case SIGNATURE_NOT_VERIFIED:
return "Signature has not yet been verified.";
case SIGNATURE_NOT_FOUND:
return "Signature not found.";
default:
return "Unknown Validation Failure.";
}
}
static const char *getReadableCertState(CertificateValidationStatus cert_vs)
{
switch (cert_vs) {
case CERTIFICATE_TRUSTED:
return "Certificate is Trusted.";
case CERTIFICATE_UNTRUSTED_ISSUER:
return "Certificate issuer isn't Trusted.";
case CERTIFICATE_UNKNOWN_ISSUER:
return "Certificate issuer is unknown.";
case CERTIFICATE_REVOKED:
return "Certificate has been Revoked.";
case CERTIFICATE_EXPIRED:
return "Certificate has Expired";
case CERTIFICATE_NOT_VERIFIED:
return "Certificate has not yet been verified.";
default:
return "Unknown issue with Certificate or corrupted data.";
}
}
static std::string getReadableTime(time_t unix_time)
{
std::stringstream stringStream;
const std::tm tm = *std::localtime(&unix_time);
stringStream << std::put_time(&tm, "%b %d %Y %H:%M:%S");
return stringStream.str();
}
static std::string_view trim(std::string_view input)
{
size_t first = input.find_first_not_of(" \t");
if (first == std::string_view::npos) {
return {};
}
size_t last = input.find_last_not_of(" \t");
return input.substr(first, last - first + 1);
}
static std::vector<std::string> parseAssertSignerFile(std::string_view input)
{
std::fstream file(std::string { input });
std::vector<std::string> lines;
std::string line;
while (std::getline(file, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
std::string_view trimmedLine = trim(line);
if (!trimmedLine.empty()) {
lines.emplace_back(trimmedLine);
}
}
return lines;
}
static std::vector<std::string> parseAssertSigner(std::string_view input)
{
if (std::filesystem::exists(input)) {
return parseAssertSignerFile(input);
}
return std::vector<std::string> { std::string { input } };
}
static bool dumpSignature(int sig_num, FormFieldSignature *s, const char *filename)
{
const std::vector<unsigned char> &signature = s->getSignature();
if (signature.empty()) {
printf("Cannot dump signature #%d\n", sig_num);
return false;
}
const std::string filenameWithExtension = GooString::format("{0:s}.sig", gbasename(filename).c_str());
const std::string sig_numString = std::to_string(sig_num);
const std::string path = filenameWithExtension + sig_numString;
printf("Signature #%d (%lu bytes) => %s\n", sig_num, signature.size(), path.c_str());
std::ofstream outfile(path.c_str(), std::ofstream::binary);
outfile.write(reinterpret_cast<const char *>(signature.data()), signature.size());
outfile.close();
return true;
}
static GooString nssDir;
static GooString nssPassword;
static char ownerPassword[33] = "\001";
static char userPassword[33] = "\001";
static bool printVersion = false;
static bool printHelp = false;
static bool printCryptoSignBackends = false;
static bool dontVerifyCert = false;
#if ENABLE_GPGME
static bool allowPgp = GpgSignatureConfiguration::arePgpSignaturesAllowed();
#else
static bool allowPgp = false;
#endif
static bool noOCSPRevocationCheck = false;
static bool noAppearance = false;
static bool dumpSignatures = false;
static bool etsiCAdESdetached = false;
static char backendString[256] = "";
static char signatureName[256] = "";
static char certNickname[256] = "";
static char password[256] = "";
static char digestName[256] = "SHA256";
static GooString reason;
static bool listNicknames = false;
static bool addNewSignature = false;
static bool useAIACertFetch = false;
static GooString newSignatureFieldName;
static char assertSigner[256] = "";
static const ArgDesc argDesc[] = {
{ .arg = "-nssdir", .kind = argGooString, .val = &nssDir, .size = 0, .usage = "path to directory of libnss3 database" },
{ .arg = "-nss-pwd", .kind = argGooString, .val = &nssPassword, .size = 0, .usage = "password to access the NSS database (if any)" },
{ .arg = "-nocert", .kind = argFlag, .val = &dontVerifyCert, .size = 0, .usage = "don't perform certificate validation" },
{ .arg = "-no-ocsp", .kind = argFlag, .val = &noOCSPRevocationCheck, .size = 0, .usage = "don't perform online OCSP certificate revocation check" },
{ .arg = "-no-appearance", .kind = argFlag, .val = &noAppearance, .size = 0, .usage = "don't add appearance information when signing existing fields" },
{ .arg = "-aia", .kind = argFlag, .val = &useAIACertFetch, .size = 0, .usage = "use Authority Information Access (AIA) extension for certificate fetching" },
{ .arg = "-assert-signer",
.kind = argString,
.val = &assertSigner,
.size = 256,
.usage = "This option checks whether the signature covering the full document been made with the specified key. The key is either specified as a fingerprint or a file listing fingerprints. The fingerprint must "
"be given or listed in compact format (no colons or spaces in between). If fpr_or_file specifies a file, empty lines are ignored as well as all lines starting with a hash sign. Only available for GnuPG backend." },
{ .arg = "-dump", .kind = argFlag, .val = &dumpSignatures, .size = 0, .usage = "dump all signatures into current directory" },
{ .arg = "-add-signature", .kind = argFlag, .val = &addNewSignature, .size = 0, .usage = "adds a new signature to the document" },
{ .arg = "-new-signature-field-name", .kind = argGooString, .val = &newSignatureFieldName, .size = 0, .usage = "field name used for the newly added signature. A random ID will be used if empty" },
{ .arg = "-sign", .kind = argString, .val = &signatureName, .size = 256, .usage = "sign the document in the given signature field (by name or number)" },
{ .arg = "-etsi", .kind = argFlag, .val = &etsiCAdESdetached, .size = 0, .usage = "create a signature of type ETSI.CAdES.detached instead of adbe.pkcs7.detached" },
{ .arg = "-backend", .kind = argString, .val = &backendString, .size = 256, .usage = "use given backend for signing/verification" },
{ .arg = "-enable-pgp", .kind = argFlag, .val = &allowPgp, .size = 0, .usage = "Enable pgp signatures in the GnuPG backend. Only available for GnuPG backend" },
{ .arg = "-nick", .kind = argString, .val = &certNickname, .size = 256, .usage = "use the certificate with the given nickname/fingerprint for signing" },
{ .arg = "-kpw", .kind = argString, .val = &password, .size = 256, .usage = "password for the signing key (might be missing if the key isn't password protected)" },
{ .arg = "-digest", .kind = argString, .val = &digestName, .size = 256, .usage = "name of the digest algorithm (default: SHA256)" },
{ .arg = "-reason", .kind = argGooString, .val = &reason, .size = 0, .usage = "reason for signing (default: no reason given)" },
{ .arg = "-list-nicks", .kind = argFlag, .val = &listNicknames, .size = 0, .usage = "list available nicknames in the NSS database" },
{ .arg = "-list-backends", .kind = argFlag, .val = &printCryptoSignBackends, .size = 0, .usage = "print cryptographic signature backends" },
{ .arg = "-opw", .kind = argString, .val = ownerPassword, .size = sizeof(ownerPassword), .usage = "owner password (for encrypted files)" },
{ .arg = "-upw", .kind = argString, .val = userPassword, .size = sizeof(userPassword), .usage = "user password (for encrypted files)" },
{ .arg = "-v", .kind = argFlag, .val = &printVersion, .size = 0, .usage = "print copyright and version info" },
{ .arg = "-h", .kind = argFlag, .val = &printHelp, .size = 0, .usage = "print usage information" },
{ .arg = "-help", .kind = argFlag, .val = &printHelp, .size = 0, .usage = "print usage information" },
{ .arg = "--help", .kind = argFlag, .val = &printHelp, .size = 0, .usage = "print usage information" },
{ .arg = "-?", .kind = argFlag, .val = &printHelp, .size = 0, .usage = "print usage information" },
{}
};
static void print_version_usage(bool usage)
{
fprintf(stderr, "pdfsig version %s\n", PACKAGE_VERSION);
fprintf(stderr, "%s\n", popplerCopyright);
fprintf(stderr, "%s\n", xpdfCopyright);
if (usage) {
printUsage("pdfsig", "<PDF-file> [<output-file>]", argDesc);
}
}
static void print_backends()
{
fprintf(stderr, "pdfsig backends:\n");
for (const auto &backend : CryptoSign::Factory::getAvailable()) {
switch (backend) {
case CryptoSign::Backend::Type::NSS3:
fprintf(stderr, "NSS");
break;
case CryptoSign::Backend::Type::GPGME:
fprintf(stderr, "GPG");
break;
}
if (backend == CryptoSign::Factory::getActive()) {
fprintf(stderr, " (active)\n");
} else {
fprintf(stderr, "\n");
}
}
}
static std::vector<std::unique_ptr<X509CertificateInfo>> getAvailableSigningCertificates(bool *error)
{
#if ENABLE_NSS3
bool wrongPassword = false;
bool passwordNeeded = false;
auto passwordCallback = [&passwordNeeded, &wrongPassword](const char *) -> char * {
static bool firstTime = true;
if (!firstTime) {
wrongPassword = true;
return nullptr;
}
firstTime = false;
if (!nssPassword.empty()) {
return strdup(nssPassword.c_str());
}
passwordNeeded = true;
return nullptr;
};
NSSSignatureConfiguration::setNSSPasswordCallback(passwordCallback);
#endif
auto backend = CryptoSign::Factory::createActive();
if (!backend) {
*error = true;
printf("No backends for cryptographic signatures available");
return {};
}
std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = backend->getAvailableSigningCertificates();
#if ENABLE_NSS3
NSSSignatureConfiguration::setNSSPasswordCallback({});
if (passwordNeeded) {
*error = true;
printf("Password is needed to access the NSS database.\n");
printf("\tPlease provide one with -nss-pwd.\n");
return {};
}
if (wrongPassword) {
*error = true;
printf("Password was not accepted to open the NSS database.\n");
printf("\tPlease provide the correct one with -nss-pwd.\n");
return {};
}
#endif
*error = false;
return vCerts;
}
static std::string locationToString(KeyLocation location)
{
switch (location) {
case KeyLocation::Unknown:
return {};
case KeyLocation::Other:
return "(Other)";
case KeyLocation::Computer:
return "(Computer)";
case KeyLocation::HardwareToken:
return "(Hardware Token)";
}
return {};
}
static const char *typeToString(CertificateType type)
{
switch (type) {
case CertificateType::PGP:
return "PGP";
case CertificateType::X509:
return "S/Mime";
}
return "";
}
static std::string TextStringToUTF8(const std::string &str)
{
const UnicodeMap *utf8Map = globalParams->getUtf8Map();
std::vector<Unicode> u = TextStringToUCS4(str);
std::string convertedStr;
for (auto &c : u) {
char buf[8];
const int n = utf8Map->mapUnicode(c, buf, sizeof(buf));
convertedStr.append(buf, n);
}
return convertedStr;
}
int main(int argc, char *argv[])
{
globalParams = std::make_unique<GlobalParams>();
Win32Console win32Console(&argc, &argv);
const bool ok = parseArgs(argDesc, &argc, argv);
if (!ok) {
print_version_usage(true);
return 99;
}
if (printVersion) {
print_version_usage(false);
return 0;
}
if (printHelp) {
print_version_usage(true);
return 0;
}
if (strlen(backendString) > 0) {
auto backend = CryptoSign::Factory::typeFromString(backendString);
if (backend) {
CryptoSign::Factory::setPreferredBackend(backend.value());
} else {
fprintf(stderr, "Unsupported backend\n");
return 98;
}
}
if (printCryptoSignBackends) {
print_backends();
return 0;
}
#if ENABLE_NSS3
NSSSignatureConfiguration::setNSSDir(nssDir);
#endif
#if ENABLE_GPGME
GpgSignatureConfiguration::setPgpSignaturesAllowed(allowPgp);
#else
if (allowPgp) {
printf("Pgp support not enabled in this build.\n");
return 99;
}
#endif
if (listNicknames) {
bool getCertsError;
const std::vector<std::unique_ptr<X509CertificateInfo>> vCerts = getAvailableSigningCertificates(&getCertsError);
if (getCertsError) {
return 2;
}
if (vCerts.empty()) {
printf("There are no certificates available.\n");
} else {
printf("Certificate nicknames available:\n");
for (const auto &cert : vCerts) {
const GooString &nick = cert->getNickName();
const auto location = locationToString(cert->getKeyLocation());
printf("%s %s %s %s\n", nick.c_str(), (cert->isQualified() ? "(*)" : " "), location.c_str(), allowPgp ? typeToString(cert->getCertificateType()) : "");
}
}
return 0;
}
if (argc < 2) {
// no filename was given
print_version_usage(true);
return 99;
}
std::unique_ptr<GooString> fileName = std::make_unique<GooString>(argv[1]);
std::optional<GooString> ownerPW, userPW;
if (ownerPassword[0] != '\001') {
ownerPW = GooString(ownerPassword);
}
if (userPassword[0] != '\001') {
userPW = GooString(userPassword);
}
// open PDF file
std::unique_ptr<PDFDoc> doc(PDFDocFactory().createPDFDoc(*fileName, ownerPW, userPW));
if (!doc->isOk()) {
return 1;
}
std::optional<unsigned int> signatureNumber;
if (strlen(signatureName) > 0) {
signatureNumber = atoi(signatureName);
if (signatureNumber == 0) {
signatureNumber = {};
}
}
if (addNewSignature && signatureNumber.has_value()) {
// incompatible options
print_version_usage(true);
return 99;
}
if (addNewSignature) {
if (argc == 2) {
fprintf(stderr, "An output filename for the signed document must be given\n");
return 2;
}
if (strlen(certNickname) == 0) {
printf("A nickname of the signing certificate must be given\n");
return 2;
}
if (etsiCAdESdetached) {
printf("-etsi is not supported yet with -add-signature\n");
printf("Please file a bug report if this is important for you\n");
return 2;
}
if (digestName != std::string("SHA256")) {
printf("Only digest SHA256 is supported at the moment with -add-signature\n");
printf("Please file a bug report if this is important for you\n");
return 2;
}
if (doc->getPage(1) == nullptr) {
printf("Error getting first page of the document.\n");
return 2;
}
bool getCertsError;
// We need to call this otherwise NSS spins forever
getAvailableSigningCertificates(&getCertsError);
if (getCertsError) {
return 2;
}
const auto rs = std::unique_ptr<GooString>(reason.toStr().empty() ? nullptr : std::make_unique<GooString>(utf8ToUtf16WithBom(reason.toStr())));
if (newSignatureFieldName.empty()) {
// Create a random field name, it could be anything but 32 hex numbers should
// hopefully give us something that is not already in the document
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distrib(1, 15);
for (int i = 0; i < 32; ++i) {
const int value = distrib(gen);
newSignatureFieldName.push_back(value < 10 ? 48 + value : 65 + (value - 10));
}
}
// We don't provide a way to customize the UI from pdfsig for now
const auto failure = doc->sign(std::string { argv[2] }, std::string { certNickname }, std::string { password }, newSignatureFieldName.copy(), /*page*/ 1,
/*rect */ { 0, 0, 0, 0 }, /*signatureText*/ {}, /*signatureTextLeft*/ {}, /*fontSize */ 0, /*leftFontSize*/ 0,
/*fontColor*/ {}, /*borderWidth*/ 0, /*borderColor*/ {}, /*backgroundColor*/ {}, rs.get(), /* location */ nullptr, /* image path */ "", ownerPW, userPW);
return !failure.has_value() ? 0 : 3;
}
const std::vector<FormFieldSignature *> signatures = doc->getSignatureFields();
const unsigned int sigCount = signatures.size();
if (!signatureNumber.has_value() && strlen(signatureName)) {
for (unsigned int i = 0; i < sigCount; i++) {
const GooString *goo = signatures.at(i)->getCreateWidget()->getField()->getFullyQualifiedName();
if (!goo) {
continue;
}
const std::string name = TextStringToUTF8(goo->toStr());
if (name == signatureName) {
signatureNumber = i + 1;
break;
}
}
if (!signatureNumber.has_value()) {
fprintf(stderr, "Did not find signature field with name: %s\n", signatureName);
return 2;
}
}
if (signatureNumber.has_value()) {
// We are signing an existing signature field
if (argc == 2) {
fprintf(stderr, "An output filename for the signed document must be given\n");
return 2;
}
if (signatureNumber > sigCount) {
printf("File '%s' does not contain a signature with number %d\n", fileName->c_str(), *signatureNumber);
return 2;
}
if (strlen(certNickname) == 0) {
printf("A nickname of the signing certificate must be given\n");
return 2;
}
if (digestName != std::string("SHA256")) {
printf("Only digest SHA256 is supported at the moment\n");
printf("Please file a bug report if this is important for you\n");
return 2;
}
bool getCertsError;
// We need to call this otherwise NSS spins forever
getAvailableSigningCertificates(&getCertsError);
if (getCertsError) {
return 2;
}
FormFieldSignature *ffs = signatures.at(*signatureNumber - 1);
auto [sig, file_size] = ffs->getCheckedSignature();
if (sig) {
printf("Signature number %d is already signed\n", *signatureNumber);
return 2;
}
if (etsiCAdESdetached) {
ffs->setSignatureType(CryptoSign::SignatureType::ETSI_CAdES_detached);
}
const auto rs = std::unique_ptr<GooString>(reason.toStr().empty() ? nullptr : std::make_unique<GooString>(utf8ToUtf16WithBom(reason.toStr())));
if (ffs->getNumWidgets() != 1) {
printf("Unexpected number of widgets for the signature: %d\n", ffs->getNumWidgets());
return 2;
}
#ifdef HAVE_GETTEXT
if (!noAppearance) {
setlocale(LC_ALL, "");
bindtextdomain("pdfsig", CMAKE_INSTALL_LOCALEDIR);
textdomain("pdfsig");
}
#endif
auto *fws = static_cast<FormWidgetSignature *>(ffs->getWidget(0));
auto backend = CryptoSign::Factory::createActive();
auto sigHandler = backend->createSigningHandler(certNickname, HashAlgorithm::Sha256);
std::unique_ptr<X509CertificateInfo> certInfo = sigHandler->getCertificateInfo();
if (!certInfo) {
fprintf(stderr, "signDocument: error getting signature info\n");
return 2;
}
const std::string signerName = certInfo->getSubjectInfo().commonName;
const std::string timestamp = timeToStringWithFormat(nullptr, "%Y.%m.%d %H:%M:%S %z");
const AnnotColor blackColor(0, 0, 0);
const std::string signatureText(GooString::format(_("Digitally signed by {0:s}"), signerName.c_str()) + "\n" + GooString::format(_("Date: {0:s}"), timestamp.c_str()));
const auto gSignatureText = std::make_unique<GooString>((signatureText.empty() || noAppearance) ? "" : utf8ToUtf16WithBom(signatureText));
const auto gSignatureLeftText = std::make_unique<GooString>((signerName.empty() || noAppearance) ? "" : utf8ToUtf16WithBom(signerName));
const auto failure = fws->signDocumentWithAppearance(argv[2], std::string { certNickname }, std::string { password }, rs.get(), nullptr, {}, {}, *gSignatureText, *gSignatureLeftText, 0, 0, std::make_unique<AnnotColor>(blackColor));
return !failure.has_value() ? 0 : 3;
}
if (argc > 2) {
// We are not signing and more than 1 filename was given
print_version_usage(true);
return 99;
}
if (sigCount >= 1) {
if (dumpSignatures) {
printf("Dumping Signatures: %u\n", sigCount);
for (unsigned int i = 0; i < sigCount; i++) {
const bool dumpingOk = dumpSignature(i, signatures.at(i), fileName->c_str());
if (!dumpingOk) {
// for now, do nothing. We have logged a message
// to the user before returning false in dumpSignature
// and it is possible to have "holes" in the signatures
continue;
}
}
return 0;
}
printf("Digital Signature Info of: %s\n", fileName->c_str());
} else {
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() == CryptoSign::SignatureType::unsigned_signature_field) {
continue;
}
signatureInfos[i] = ffs->validateSignatureAsync(!dontVerifyCert, false, -1 /* now */, !noOCSPRevocationCheck, useAIACertFetch, {});
}
bool totalDocumentSigned = false;
bool oneSignatureInvalid = false;
std::string totalDocumentNick;
for (unsigned int i = 0; i < sigCount; i++) {
FormFieldSignature *ffs = signatures.at(i);
printf("Signature #%u:\n", i + 1);
const GooString *goo = ffs->getCreateWidget()->getField()->getFullyQualifiedName();
if (goo) {
const std::string name = TextStringToUTF8(goo->toStr());
printf(" - Signature Field Name: %s\n", name.c_str());
}
if (ffs->getSignatureType() == CryptoSign::SignatureType::unsigned_signature_field) {
printf(" The signature form field is not signed.\n");
continue;
}
const SignatureInfo *sig_info = signatureInfos[i];
CertificateValidationStatus certificateStatus = ffs->validateSignatureResult();
if (sig_info->getSignatureValStatus() == SIGNATURE_DECODING_ERROR) {
printf(" - Decoding failed\n");
oneSignatureInvalid = true;
continue;
}
printf(" - Signer Certificate Common Name: %s\n", sig_info->getSignerName().c_str());
if (CryptoSign::Factory::getActive() == CryptoSign::Backend::Type::GPGME) {
printf(" - Signer fingerprint: %s\n", sig_info->getCertificateInfo()->getNickName().c_str());
}
printf(" - Signer full Distinguished Name: %s\n", sig_info->getSubjectDN().c_str());
printf(" - Signing Time: %s\n", getReadableTime(sig_info->getSigningTime()).c_str());
printf(" - Signing Hash Algorithm: ");
switch (sig_info->getHashAlgorithm()) {
case HashAlgorithm::Md2:
printf("MD2\n");
break;
case HashAlgorithm::Md5:
printf("MD5\n");
break;
case HashAlgorithm::Sha1:
printf("SHA1\n");
break;
case HashAlgorithm::Sha256:
printf("SHA-256\n");
break;
case HashAlgorithm::Sha384:
printf("SHA-384\n");
break;
case HashAlgorithm::Sha512:
printf("SHA-512\n");
break;
case HashAlgorithm::Sha224:
printf("SHA-224\n");
break;
default:
printf("unknown\n");
}
printf(" - Signature Type: ");
switch (ffs->getSignatureType()) {
case CryptoSign::SignatureType::adbe_pkcs7_sha1:
printf("adbe.pkcs7.sha1\n");
break;
case CryptoSign::SignatureType::adbe_pkcs7_detached:
printf("adbe.pkcs7.detached\n");
break;
case CryptoSign::SignatureType::ETSI_CAdES_detached:
printf("ETSI.CAdES.detached\n");
break;
case CryptoSign::SignatureType::g10c_pgp_signature_detached:
printf("g10c.pgp.signature.detached\n");
break;
case CryptoSign::SignatureType::unknown_signature_type:
case CryptoSign::SignatureType::unsigned_signature_field: /*shouldn't happen*/
printf("unknown\n");
}
const std::vector<Goffset> ranges = ffs->getSignedRangeBounds();
if (ranges.size() == 4) {
printf(" - Signed Ranges: [%lld - %lld], [%lld - %lld]\n", ranges[0], ranges[1], ranges[2], ranges[3]);
auto [signature, checked_file_size] = signatures.at(i)->getCheckedSignature();
if (signature && checked_file_size == ranges[3]) {
if (totalDocumentSigned) {
printf("multiple signatures is covering entire document. Impossible");
return 2;
}
if (sig_info->getCertificateInfo()) {
totalDocumentSigned = true;
totalDocumentNick = sig_info->getCertificateInfo()->getNickName().toStr();
}
printf(" - Total document signed\n");
} else {
printf(" - Not total document signed\n");
}
}
printf(" - Signature Validation: %s\n", getReadableSigState(sig_info->getSignatureValStatus()));
if (sig_info->getSignatureValStatus() != SIGNATURE_VALID) {
oneSignatureInvalid = true;
continue;
}
if (dontVerifyCert) {
continue;
}
printf(" - Certificate Validation: %s\n", getReadableCertState(certificateStatus));
}
if (!oneSignatureInvalid) {
if (strlen(assertSigner) > 1 && CryptoSign::Factory::getActive() == CryptoSign::Backend::Type::GPGME) {
if (!totalDocumentSigned) {
printf(" - Assert signer: Total document not signed\n");
return 1;
}
std::vector<std::string> assertKeys = parseAssertSigner(assertSigner);
if (std::ranges::find(assertKeys, totalDocumentNick) == std::end(assertKeys)) {
printf(" - Assert signer: Key not in list\n");
// we do't have the key in the assert list, so error out
return 1;
}
}
return 0;
}
return 1;
}