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 | |
22 | class TestSignatureBasics : public QObject |
23 | { |
24 | Q_OBJECT |
25 | public: |
26 | explicit TestSignatureBasics(QObject *parent = nullptr) : QObject(parent) { } |
27 | |
28 | private: |
29 | std::unique_ptr<PDFDoc> doc; |
30 | private 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 | |
41 | void 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 | |
55 | void 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 | |
79 | void TestSignatureBasics::cleanupTestCase() |
80 | { |
81 | globalParams.reset(); |
82 | } |
83 | |
84 | void 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 | |
96 | void 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 | |
108 | void 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 | |
151 | void 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 | |
178 | QTEST_GUILESS_MAIN(TestSignatureBasics) |
179 | #include "check_signature_basics.moc" |
180 | |