1//========================================================================
2//
3// check_signature_basics.cpp
4//
5// This file is licensed under the GPLv2 or later
6//
7// Copyright 2023, 2024 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
8//========================================================================
9
10// Simple tests of reading signatures
11//
12// Note that this does not check the actual validity because
13// that will have an expiry date, and adding time bombs to unit tests is
14// probably not a good idea.
15#include <QtTest/QTest>
16#include "PDFDoc.h"
17#include "GlobalParams.h"
18#include "SignatureInfo.h"
19#include "CryptoSignBackend.h"
20#include "config.h"
21
22class TestSignatureBasics : public QObject
23{
24 Q_OBJECT
25public:
26 explicit TestSignatureBasics(QObject *parent = nullptr) : QObject(parent) { }
27
28private:
29 std::unique_ptr<PDFDoc> doc;
30private Q_SLOTS:
31 void init();
32 void initTestCase_data();
33 void initTestCase() { }
34 void cleanupTestCase();
35 void testSignatureCount();
36 void testSignatureSizes();
37 void testSignerInfo(); // names and stuff
38 void testSignedRanges();
39};
40
41void TestSignatureBasics::init()
42{
43#ifdef ENABLE_SIGNATURES
44 QFETCH_GLOBAL(CryptoSign::Backend::Type, backend);
45 CryptoSign::Factory::setPreferredBackend(backend);
46 QCOMPARE(CryptoSign::Factory::getActive(), backend);
47#endif
48
49 globalParams = std::make_unique<GlobalParams>();
50 doc = std::make_unique<PDFDoc>(args: std::make_unique<GooString>(TESTDATADIR "/unittestcases/pdf-signature-sample-2sigs.pdf"));
51 QVERIFY(doc);
52 QVERIFY(doc->isOk());
53}
54
55void TestSignatureBasics::initTestCase_data()
56{
57 QTest::addColumn<CryptoSign::Backend::Type>(name: "backend");
58
59#ifdef ENABLE_SIGNATURES
60 const auto availableBackends = CryptoSign::Factory::getAvailable();
61
62# ifdef ENABLE_NSS3
63 if (std::find(first: availableBackends.begin(), last: availableBackends.end(), val: CryptoSign::Backend::Type::NSS3) != availableBackends.end()) {
64 QTest::newRow(dataTag: "nss") << CryptoSign::Backend::Type::NSS3;
65 } else {
66 QWARN("Compiled with NSS3, but NSS not functional");
67 }
68# endif
69# ifdef ENABLE_GPGME
70 if (std::find(availableBackends.begin(), availableBackends.end(), CryptoSign::Backend::Type::GPGME) != availableBackends.end()) {
71 QTest::newRow("gpg") << CryptoSign::Backend::Type::GPGME;
72 } else {
73 QWARN("Compiled with GPGME, but GPGME not functional");
74 }
75# endif
76#endif
77}
78
79void TestSignatureBasics::cleanupTestCase()
80{
81 globalParams.reset();
82}
83
84void TestSignatureBasics::testSignatureCount()
85{
86 QVERIFY(doc);
87 auto signatureFields = doc->getSignatureFields();
88 QCOMPARE(signatureFields.size(), 4);
89 // count active signatures
90 QVERIFY(signatureFields[0]->getSignature());
91 QVERIFY(signatureFields[1]->getSignature());
92 QVERIFY(!signatureFields[2]->getSignature());
93 QVERIFY(!signatureFields[3]->getSignature());
94}
95
96void TestSignatureBasics::testSignatureSizes()
97{
98 auto signatureFields = doc->getSignatureFields();
99 // These are not the actual signature lengths, but rather
100 // the length of the signature field, which is likely
101 // a padded field. At least the pdf specification suggest to pad
102 // the field.
103 // Poppler before 23.04 did not have a padded field, later versions do.
104 QCOMPARE(signatureFields[0]->getSignature()->getLength(), 10230); // Signature data size is 2340
105 QCOMPARE(signatureFields[1]->getSignature()->getLength(), 10196); // Signature data size is 2340
106}
107
108void TestSignatureBasics::testSignerInfo()
109{
110 auto signatureFields = doc->getSignatureFields();
111 QCOMPARE(signatureFields[0]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature0_B_" });
112 QCOMPARE(signatureFields[0]->getSignatureType(), ETSI_CAdES_detached);
113 auto siginfo0 = signatureFields[0]->validateSignatureAsync(doVerifyCert: false, forceRevalidation: false, validationTime: -1 /* now */, ocspRevocationCheck: false, enableAIA: false, doneCallback: {});
114 signatureFields[0]->validateSignatureResult();
115#ifdef ENABLE_SIGNATURES
116 QCOMPARE(siginfo0->getSignerName(), std::string { "Koch, Werner" });
117 QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Sha256);
118 QCOMPARE(siginfo0->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 2048 / 8);
119#else
120 QCOMPARE(siginfo0->getSignerName(), std::string {});
121 QCOMPARE(siginfo0->getHashAlgorithm(), HashAlgorithm::Unknown);
122#endif
123 QCOMPARE(siginfo0->getSigningTime(), time_t(1677570911));
124
125 QCOMPARE(signatureFields[1]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature1_B_" });
126 QCOMPARE(signatureFields[1]->getSignatureType(), ETSI_CAdES_detached);
127 auto siginfo1 = signatureFields[1]->validateSignatureAsync(doVerifyCert: false, forceRevalidation: false, validationTime: -1 /* now */, ocspRevocationCheck: false, enableAIA: false, doneCallback: {});
128 signatureFields[1]->validateSignatureResult();
129#ifdef ENABLE_SIGNATURES
130 QCOMPARE(siginfo1->getSignerName(), std::string { "Koch, Werner" });
131 QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Sha256);
132 QFETCH_GLOBAL(CryptoSign::Backend::Type, backend);
133 if (backend == CryptoSign::Backend::Type::GPGME) {
134 QCOMPARE(siginfo1->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 2048 / 8);
135 } else if (backend == CryptoSign::Backend::Type::NSS3) {
136 // Not fully sure why it is zero here, but it seems to be.
137 QCOMPARE(siginfo1->getCertificateInfo()->getPublicKeyInfo().publicKeyStrength, 0);
138 }
139#else
140 QCOMPARE(siginfo1->getSignerName(), std::string {});
141 QCOMPARE(siginfo1->getHashAlgorithm(), HashAlgorithm::Unknown);
142#endif
143 QCOMPARE(siginfo1->getSigningTime(), time_t(1677840601));
144
145 QCOMPARE(signatureFields[2]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature2_B_" });
146 QCOMPARE(signatureFields[2]->getSignatureType(), unsigned_signature_field);
147 QCOMPARE(signatureFields[3]->getCreateWidget()->getField()->getFullyQualifiedName()->toStr(), std::string { "P2.AnA_Signature3_B_" });
148 QCOMPARE(signatureFields[3]->getSignatureType(), unsigned_signature_field);
149}
150
151void TestSignatureBasics::testSignedRanges()
152{
153 auto signatureFields = doc->getSignatureFields();
154
155 Goffset size0;
156 auto sig0 = signatureFields[0]->getCheckedSignature(checkedFileSize: &size0);
157 QVERIFY(sig0);
158 auto ranges0 = signatureFields[0]->getSignedRangeBounds();
159 QCOMPARE(ranges0.size(), 4);
160 QCOMPARE(ranges0[0], 0);
161 QCOMPARE(ranges0[1], 24890);
162 QCOMPARE(ranges0[2], 45352);
163 QCOMPARE(ranges0[3], 58529);
164 QVERIFY(ranges0[3] != size0); // signature does not cover all of it
165
166 Goffset size1;
167 auto sig1 = signatureFields[1]->getCheckedSignature(checkedFileSize: &size1);
168 QVERIFY(sig1);
169 auto ranges1 = signatureFields[1]->getSignedRangeBounds();
170 QCOMPARE(ranges1.size(), 4);
171 QCOMPARE(ranges1[0], 0);
172 QCOMPARE(ranges1[1], 59257);
173 QCOMPARE(ranges1[2], 79651);
174 QCOMPARE(ranges1[3], 92773);
175 QCOMPARE(ranges1[3], size1); // signature does cover all of it
176}
177
178QTEST_GUILESS_MAIN(TestSignatureBasics)
179#include "check_signature_basics.moc"
180

source code of poppler/qt6/tests/check_signature_basics.cpp