blob: f62229f6ca1e2555b3256411c762080a2b3c22cc [file] [log] [blame] [edit]
//========================================================================
//
// check_signature_basics.cpp
//
// This file is licensed under the GPLv2 or later
//
// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
//========================================================================
// Simple tests of reading signatures
//
// Note that this does not check the actual validity because
// that will have an expiry date, and adding time bombs to unit tests is
// probably not a good idea.
#include <QtTest/QTest>
#include "PDFDoc.h"
#include "GlobalParams.h"
#include "SignatureInfo.h"
#include "CryptoSignBackend.h"
#include "config.h"
class TestSignatureBasics : public QObject
{
Q_OBJECT
public:
explicit TestSignatureBasics(QObject *parent = nullptr) : QObject(parent) { }
std::unique_ptr<PDFDoc> doc;
private Q_SLOTS:
void init();
void initTestCase_data();
void initTestCase() { }
void cleanupTestCase();
void testSignatureCount();
void testSignatureSizes();
void testSignerInfo(); // names and stuff
void testSignedRanges();
};
void TestSignatureBasics::init()
{
#ifdef ENABLE_SIGNATURES
QFETCH_GLOBAL(CryptoSign::Backend::Type, backend);
CryptoSign::Factory::setPreferredBackend(backend);
QCOMPARE(CryptoSign::Factory::getActive(), backend);
#endif
globalParams = std::make_unique<GlobalParams>();
doc = std::make_unique<PDFDoc>(std::make_unique<GooString>(TESTDATADIR "/unittestcases/pdf-signature-sample-2sigs.pdf"));
QVERIFY(doc);
QVERIFY(doc->isOk());
}
void TestSignatureBasics::initTestCase_data()
{
QTest::addColumn<CryptoSign::Backend::Type>("backend");
#ifdef ENABLE_SIGNATURES
const auto availableBackends = CryptoSign::Factory::getAvailable();
# ifdef ENABLE_NSS3
if (std::ranges::find(availableBackends, CryptoSign::Backend::Type::NSS3) != availableBackends.end()) {
QTest::newRow("nss") << CryptoSign::Backend::Type::NSS3;
} else {
QWARN("Compiled with NSS3, but NSS not functional");
}
# endif
# ifdef ENABLE_GPGME
if (std::ranges::find(availableBackends, CryptoSign::Backend::Type::GPGME) != availableBackends.end()) {
QTest::newRow("gpg") << CryptoSign::Backend::Type::GPGME;
} else {
QWARN("Compiled with GPGME, but GPGME not functional");
}
# endif
#endif
}
void TestSignatureBasics::cleanupTestCase()
{
globalParams.reset();
}
void TestSignatureBasics::testSignatureCount()
{
QVERIFY(doc);
auto signatureFields = doc->getSignatureFields();
QCOMPARE(signatureFields.size(), 4);
// count active signatures
QVERIFY(!signatureFields[0]->getSignature().empty());
QVERIFY(!signatureFields[1]->getSignature().empty());
QVERIFY(signatureFields[2]->getSignature().empty());
QVERIFY(signatureFields[3]->getSignature().empty());
}
void TestSignatureBasics::testSignatureSizes()
{
auto signatureFields = doc->getSignatureFields();
// These are not the actual signature lengths, but rather
// the length of the signature field, which is likely
// a padded field. At least the pdf specification suggest to pad
// the field.
// Poppler before 23.04 did not have a padded field, later versions do.
QCOMPARE(signatureFields[0]->getSignature().size(), 10230); // Signature data size is 2340
QCOMPARE(signatureFields[1]->getSignature().size(), 10196); // Signature data size is 2340
}
void TestSignatureBasics::testSignerInfo()
{
auto signatureFields = doc->getSignatureFields();
QCOMPARE(signatureFields[0]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature0_B_" });
QCOMPARE(signatureFields[0]->getSignatureType(), CryptoSign::SignatureType::ETSI_CAdES_detached);
auto siginfo0 = signatureFields[0]->validateSignatureAsync(false, false, -1 /* now */, false, false, {});
signatureFields[0]->validateSignatureResult();
#ifdef ENABLE_SIGNATURES
QCOMPARE(siginfo0->getSignerName(), std::string { "Koch, Werner" });
QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Sha256);
QCOMPARE(siginfo0->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 2048 / 8);
#else
QCOMPARE(siginfo0->getSignerName(), std::string {});
QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Unknown);
#endif
QCOMPARE(siginfo0->getSigningTime(), time_t(1677570911));
QCOMPARE(signatureFields[1]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature1_B_" });
QCOMPARE(signatureFields[1]->getSignatureType(), CryptoSign::SignatureType::ETSI_CAdES_detached);
auto siginfo1 = signatureFields[1]->validateSignatureAsync(false, false, -1 /* now */, false, false, {});
signatureFields[1]->validateSignatureResult();
#ifdef ENABLE_SIGNATURES
QCOMPARE(siginfo1->getSignerName(), std::string { "Koch, Werner" });
QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Sha256);
QFETCH_GLOBAL(CryptoSign::Backend::Type, backend);
if (backend == CryptoSign::Backend::Type::GPGME) {
QCOMPARE(siginfo1->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 2048 / 8);
} else if (backend == CryptoSign::Backend::Type::NSS3) {
// Not fully sure why it is zero here, but it seems to be.
QCOMPARE(siginfo1->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 0);
}
#else
QCOMPARE(siginfo1->getSignerName(), std::string {});
QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Unknown);
#endif
QCOMPARE(siginfo1->getSigningTime(), time_t(1677840601));
QCOMPARE(signatureFields[2]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature2_B_" });
QCOMPARE(signatureFields[2]->getSignatureType(), CryptoSign::SignatureType::unsigned_signature_field);
QCOMPARE(signatureFields[3]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature3_B_" });
QCOMPARE(signatureFields[3]->getSignatureType(), CryptoSign::SignatureType::unsigned_signature_field);
}
void TestSignatureBasics::testSignedRanges()
{
auto signatureFields = doc->getSignatureFields();
Goffset size0;
auto sig0 = signatureFields[0]->getCheckedSignature(&size0);
QVERIFY(sig0);
auto ranges0 = signatureFields[0]->getSignedRangeBounds();
QCOMPARE(ranges0.size(), 4);
QCOMPARE(ranges0[0], 0);
QCOMPARE(ranges0[1], 24890);
QCOMPARE(ranges0[2], 45352);
QCOMPARE(ranges0[3], 58529);
QVERIFY(ranges0[3] != size0); // signature does not cover all of it
Goffset size1;
auto sig1 = signatureFields[1]->getCheckedSignature(&size1);
QVERIFY(sig1);
auto ranges1 = signatureFields[1]->getSignedRangeBounds();
QCOMPARE(ranges1.size(), 4);
QCOMPARE(ranges1[0], 0);
QCOMPARE(ranges1[1], 59257);
QCOMPARE(ranges1[2], 79651);
QCOMPARE(ranges1[3], 92773);
QCOMPARE(ranges1[3], size1); // signature does cover all of it
}
QTEST_GUILESS_MAIN(TestSignatureBasics)
#include "check_signature_basics.moc"