1 //========================================================================
2 //
3 // SignatureHandler.cc
4 //
5 // This file is licensed under the GPLv2 or later
6 //
7 // Copyright 2015, 2016 André Guerreiro <aguerreiro1985@gmail.com>
8 // Copyright 2015 André Esser <bepandre@hotmail.com>
9 // Copyright 2015, 2016, 2018, 2019, 2021 Albert Astals Cid <aacid@kde.org>
10 // Copyright 2015 Markus Kilås <digital@markuspage.com>
11 // Copyright 2017 Sebastian Rasmussen <sebras@gmail.com>
12 // Copyright 2017 Hans-Ulrich Jüttner <huj@froreich-bioscientia.de>
13 // Copyright 2018 Chinmoy Ranjan Pradhan <chinmoyrp65@protonmail.com>
14 // Copyright 2018 Oliver Sander <oliver.sander@tu-dresden.de>
15 // Copyright 2020 Thorsten Behrens <Thorsten.Behrens@CIB.de>
16 // Copyright 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by Technische Universität Dresden
17 // Copyright 2021 Theofilos Intzoglou <int.teo@gmail.com>
18 // Copyright 2021 Marek Kasik <mkasik@redhat.com>
19 //
20 //========================================================================
21
22 #include <config.h>
23
24 #include "SignatureHandler.h"
25 #include "goo/gmem.h"
26
27 #include <dirent.h>
28 #include <Error.h>
29
30 /* NSS headers */
31 #include <secmod.h>
32 #include <keyhi.h>
33 #include <secder.h>
34 #include <pk11pub.h>
35 #include <secpkcs7.h>
36
37 #include <cert.h>
38 #include <hasht.h>
39 #include <secerr.h>
40 #include <sechash.h>
41 #include <cms.h>
42 #include <cmst.h>
43
44 // ASN.1 used in the (much simpler) time stamp request. From RFC3161
45 // and other sources.
46
47 /*
48 AlgorithmIdentifier ::= SEQUENCE {
49 algorithm OBJECT IDENTIFIER,
50 parameters ANY DEFINED BY algorithm OPTIONAL }
51 -- contains a value of the type
52 -- registered for use with the
53 -- algorithm object identifier value
54
55 MessageImprint ::= SEQUENCE {
56 hashAlgorithm AlgorithmIdentifier,
57 hashedMessage OCTET STRING }
58 */
59
60 struct MessageImprint
61 {
62 SECAlgorithmID hashAlgorithm;
63 SECItem hashedMessage;
64 };
65
66 /*
67 Extension ::= SEQUENCE {
68 extnID OBJECT IDENTIFIER,
69 critical BOOLEAN DEFAULT FALSE,
70 extnValue OCTET STRING }
71 */
72
73 struct Extension
74 {
75 SECItem const extnID;
76 SECItem const critical;
77 SECItem const extnValue;
78 };
79
80 /*
81 Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
82 */
83
84 /*
85 TSAPolicyId ::= OBJECT IDENTIFIER
86
87 TimeStampReq ::= SEQUENCE {
88 version INTEGER { v1(1) },
89 messageImprint MessageImprint,
90 --a hash algorithm OID and the hash value of the data to be
91 --time-stamped
92 reqPolicy TSAPolicyId OPTIONAL,
93 nonce INTEGER OPTIONAL,
94 certReq BOOLEAN DEFAULT FALSE,
95 extensions [0] IMPLICIT Extensions OPTIONAL }
96 */
97
98 struct TimeStampReq
99 {
100 SECItem version;
101 MessageImprint messageImprint;
102 SECItem reqPolicy;
103 SECItem nonce;
104 SECItem certReq;
105 Extension *extensions;
106 };
107
108 /**
109 * General name, defined by RFC 3280.
110 */
111 struct GeneralName
112 {
113 CERTName name;
114 };
115
116 /**
117 * List of general names (only one for now), defined by RFC 3280.
118 */
119 struct GeneralNames
120 {
121 GeneralName names;
122 };
123
124 /**
125 * Supplies different fields to identify a certificate, defined by RFC 5035.
126 */
127 struct IssuerSerial
128 {
129 GeneralNames issuer;
130 SECItem serialNumber;
131 };
132
133 /**
134 * Supplies different fields that are used to identify certificates, defined by
135 * RFC 5035.
136 */
137 struct ESSCertIDv2
138 {
139 SECAlgorithmID hashAlgorithm;
140 SECItem certHash;
141 IssuerSerial issuerSerial;
142 };
143
144 /**
145 * This attribute uses the ESSCertIDv2 structure, defined by RFC 5035.
146 */
147 struct SigningCertificateV2
148 {
149 ESSCertIDv2 **certs;
150
SigningCertificateV2SigningCertificateV2151 SigningCertificateV2() : certs(nullptr) { }
152 };
153
154 /**
155 * GeneralName ::= CHOICE {
156 * otherName [0] OtherName,
157 * rfc822Name [1] IA5String,
158 * dNSName [2] IA5String,
159 * x400Address [3] ORAddress,
160 * directoryName [4] Name,
161 * ediPartyName [5] EDIPartyName,
162 * uniformResourceIdentifier [6] IA5String,
163 * iPAddress [7] OCTET STRING,
164 * registeredID [8] OBJECT IDENTIFIER
165 * }
166 */
167 const SEC_ASN1Template GeneralNameTemplate[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(GeneralName) }, { SEC_ASN1_INLINE, offsetof(GeneralName, name), CERT_NameTemplate, 0 }, { 0, 0, nullptr, 0 } };
168
169 /**
170 * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
171 */
172 const SEC_ASN1Template GeneralNamesTemplate[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(GeneralNames) }, { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 4, offsetof(GeneralNames, names), GeneralNameTemplate, 0 }, { 0, 0, nullptr, 0 } };
173
174 /**
175 * IssuerSerial ::= SEQUENCE {
176 * issuer GeneralNames,
177 * serialNumber CertificateSerialNumber
178 * }
179 */
180 const SEC_ASN1Template IssuerSerialTemplate[] = {
181 { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(IssuerSerial) }, { SEC_ASN1_INLINE, offsetof(IssuerSerial, issuer), GeneralNamesTemplate, 0 }, { SEC_ASN1_INTEGER, offsetof(IssuerSerial, serialNumber), nullptr, 0 }, { 0, 0, nullptr, 0 }
182 };
183
184 /**
185 * Hash ::= OCTET STRING
186 *
187 * ESSCertIDv2 ::= SEQUENCE {
188 * hashAlgorithm AlgorithmIdentifier DEFAULT {algorithm id-sha256},
189 * certHash Hash,
190 * issuerSerial IssuerSerial OPTIONAL
191 * }
192 */
193
194 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
195
196 const SEC_ASN1Template ESSCertIDv2Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(ESSCertIDv2) },
197 { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(ESSCertIDv2, hashAlgorithm), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate), 0 },
198 { SEC_ASN1_OCTET_STRING, offsetof(ESSCertIDv2, certHash), nullptr, 0 },
199 { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(ESSCertIDv2, issuerSerial), IssuerSerialTemplate, 0 },
200 { 0, 0, nullptr, 0 } };
201
202 /**
203 * SigningCertificateV2 ::= SEQUENCE {
204 * }
205 */
206 const SEC_ASN1Template SigningCertificateV2Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(SigningCertificateV2) }, { SEC_ASN1_SEQUENCE_OF, offsetof(SigningCertificateV2, certs), ESSCertIDv2Template, 0 }, { 0, 0, nullptr, 0 } };
207
208 /*
209 struct PKIStatusInfo
210 {
211 SECItem status;
212 SECItem statusString;
213 SECItem failInfo;
214 };
215
216 const SEC_ASN1Template PKIStatusInfo_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(PKIStatusInfo) },
217 { SEC_ASN1_INTEGER, offsetof(PKIStatusInfo, status), nullptr, 0 },
218 { SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, statusString), nullptr, 0 },
219 { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL, offsetof(PKIStatusInfo, failInfo), nullptr, 0 },
220 { 0, 0, nullptr, 0 } };
221
222 const SEC_ASN1Template Any_Template[] = { { SEC_ASN1_ANY, 0, nullptr, sizeof(SECItem) } };
223
224 struct TimeStampResp
225 {
226 PKIStatusInfo status;
227 SECItem timeStampToken;
228 };
229
230 const SEC_ASN1Template TimeStampResp_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(TimeStampResp) },
231 { SEC_ASN1_INLINE, offsetof(TimeStampResp, status), PKIStatusInfo_Template, 0 },
232 { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL, offsetof(TimeStampResp, timeStampToken), Any_Template, 0 },
233 { 0, 0, nullptr, 0 } };
234
235 const SEC_ASN1Template MessageImprint_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(MessageImprint) },
236 { SEC_ASN1_INLINE, offsetof(MessageImprint, hashAlgorithm), SECOID_AlgorithmIDTemplate, 0 },
237 { SEC_ASN1_OCTET_STRING, offsetof(MessageImprint, hashedMessage), nullptr, 0 },
238 { 0, 0, nullptr, 0 } };
239
240 const SEC_ASN1Template Extension_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(Extension) },
241 { SEC_ASN1_OBJECT_ID, offsetof(Extension, extnID), nullptr, 0 },
242 { SEC_ASN1_BOOLEAN, offsetof(Extension, critical), nullptr, 0 },
243 { SEC_ASN1_OCTET_STRING, offsetof(Extension, extnValue), nullptr, 0 },
244 { 0, 0, nullptr, 0 } };
245
246 const SEC_ASN1Template Extensions_Template[] = { { SEC_ASN1_SEQUENCE_OF, 0, Extension_Template, 0 } };
247
248 const SEC_ASN1Template TimeStampReq_Template[] = { { SEC_ASN1_SEQUENCE, 0, nullptr, sizeof(TimeStampReq) },
249 { SEC_ASN1_INTEGER, offsetof(TimeStampReq, version), nullptr, 0 },
250 { SEC_ASN1_INLINE, offsetof(TimeStampReq, messageImprint), MessageImprint_Template, 0 },
251 { SEC_ASN1_OBJECT_ID | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, reqPolicy), nullptr, 0 },
252 { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, nonce), nullptr, 0 },
253 { SEC_ASN1_BOOLEAN | SEC_ASN1_OPTIONAL, offsetof(TimeStampReq, certReq), nullptr, 0 },
254 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONTEXT_SPECIFIC | 0, offsetof(TimeStampReq, extensions), Extensions_Template, 0 },
255 { 0, 0, nullptr, 0 } };
256 */
257
258 // a dummy, actually
passwordCallback(PK11SlotInfo *,PRBool,void * arg)259 static char *passwordCallback(PK11SlotInfo * /*slot*/, PRBool /*retry*/, void *arg)
260 {
261 return PL_strdup(static_cast<char *>(arg));
262 }
263
shutdownNss()264 static void shutdownNss()
265 {
266 if (NSS_Shutdown() != SECSuccess) {
267 fprintf(stderr, "NSS_Shutdown failed: %s\n", PR_ErrorToString(PORT_GetError(), PR_LANGUAGE_I_DEFAULT));
268 }
269 }
270
271 // SEC_StringToOID() and NSS_CMSSignerInfo_AddUnauthAttr() are
272 // not exported from libsmime, so copy them here. Sigh.
273
my_SEC_StringToOID(SECItem * to,const char * from,PRUint32 len)274 static SECStatus my_SEC_StringToOID(SECItem *to, const char *from, PRUint32 len)
275 {
276 PRUint32 decimal_numbers = 0;
277 PRUint32 result_bytes = 0;
278 SECStatus rv;
279 PRUint8 result[1024];
280
281 static const PRUint32 max_decimal = 0xffffffff / 10;
282 static const char OIDstring[] = { "OID." };
283
284 if (!from || !to) {
285 PORT_SetError(SEC_ERROR_INVALID_ARGS);
286 return SECFailure;
287 }
288 if (!len) {
289 len = PL_strlen(from);
290 }
291 if (len >= 4 && !PL_strncasecmp(from, OIDstring, 4)) {
292 from += 4; /* skip leading "OID." if present */
293 len -= 4;
294 }
295 if (!len) {
296 bad_data:
297 PORT_SetError(SEC_ERROR_BAD_DATA);
298 return SECFailure;
299 }
300 do {
301 PRUint32 decimal = 0;
302 while (len > 0 && (*from >= '0' && *from <= '9')) {
303 PRUint32 addend = *from++ - '0';
304 --len;
305 if (decimal > max_decimal) /* overflow */
306 goto bad_data;
307 decimal = (decimal * 10) + addend;
308 if (decimal < addend) /* overflow */
309 goto bad_data;
310 }
311 if (len != 0 && *from != '.') {
312 goto bad_data;
313 }
314 if (decimal_numbers == 0) {
315 if (decimal > 2)
316 goto bad_data;
317 result[0] = decimal * 40;
318 result_bytes = 1;
319 } else if (decimal_numbers == 1) {
320 if (decimal > 40)
321 goto bad_data;
322 result[0] += decimal;
323 } else {
324 /* encode the decimal number, */
325 PRUint8 *rp;
326 PRUint32 num_bytes = 0;
327 PRUint32 tmp = decimal;
328 while (tmp) {
329 num_bytes++;
330 tmp >>= 7;
331 }
332 if (!num_bytes)
333 ++num_bytes; /* use one byte for a zero value */
334 if (num_bytes + result_bytes > sizeof result)
335 goto bad_data;
336 tmp = num_bytes;
337 rp = result + result_bytes - 1;
338 rp[tmp] = static_cast<PRUint8>(decimal & 0x7f);
339 decimal >>= 7;
340 while (--tmp > 0) {
341 rp[tmp] = static_cast<PRUint8>(decimal | 0x80);
342 decimal >>= 7;
343 }
344 result_bytes += num_bytes;
345 }
346 ++decimal_numbers;
347 if (len > 0) { /* skip trailing '.' */
348 ++from;
349 --len;
350 }
351 } while (len > 0);
352 /* now result contains result_bytes of data */
353 if (to->data && to->len >= result_bytes) {
354 to->len = result_bytes;
355 PORT_Memcpy(to->data, result, to->len);
356 rv = SECSuccess;
357 } else {
358 SECItem result_item = { siBuffer, nullptr, 0 };
359 result_item.data = result;
360 result_item.len = result_bytes;
361 rv = SECITEM_CopyItem(nullptr, to, &result_item);
362 }
363 return rv;
364 }
365
my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute ** attrs,SECOidTag oidtag,PRBool only)366 static NSSCMSAttribute *my_NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
367 {
368 SECOidData *oid;
369 NSSCMSAttribute *attr1, *attr2;
370
371 if (attrs == nullptr)
372 return nullptr;
373
374 oid = SECOID_FindOIDByTag(oidtag);
375 if (oid == nullptr)
376 return nullptr;
377
378 while ((attr1 = *attrs++) != nullptr) {
379 if (attr1->type.len == oid->oid.len && PORT_Memcmp(attr1->type.data, oid->oid.data, oid->oid.len) == 0)
380 break;
381 }
382
383 if (attr1 == nullptr)
384 return nullptr;
385
386 if (!only)
387 return attr1;
388
389 while ((attr2 = *attrs++) != nullptr) {
390 if (attr2->type.len == oid->oid.len && PORT_Memcmp(attr2->type.data, oid->oid.data, oid->oid.len) == 0)
391 break;
392 }
393
394 if (attr2 != nullptr)
395 return nullptr;
396
397 return attr1;
398 }
399
my_NSS_CMSArray_Add(PLArenaPool * poolp,void *** array,void * obj)400 static SECStatus my_NSS_CMSArray_Add(PLArenaPool *poolp, void ***array, void *obj)
401 {
402 int n = 0;
403 void **dest;
404
405 PORT_Assert(array != NULL);
406 if (array == nullptr)
407 return SECFailure;
408
409 if (*array == nullptr) {
410 dest = static_cast<void **>(PORT_ArenaAlloc(poolp, 2 * sizeof(void *)));
411 } else {
412 void **p = *array;
413 while (*p++)
414 n++;
415 dest = static_cast<void **>(PORT_ArenaGrow(poolp, *array, (n + 1) * sizeof(void *), (n + 2) * sizeof(void *)));
416 }
417
418 if (dest == nullptr)
419 return SECFailure;
420
421 dest[n] = obj;
422 dest[n + 1] = nullptr;
423 *array = dest;
424 return SECSuccess;
425 }
426
my_NSS_CMSAttribute_GetType(NSSCMSAttribute * attr)427 static SECOidTag my_NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
428 {
429 SECOidData *typetag;
430
431 typetag = SECOID_FindOID(&(attr->type));
432 if (typetag == nullptr)
433 return SEC_OID_UNKNOWN;
434
435 return typetag->offset;
436 }
437
my_NSS_CMSAttributeArray_AddAttr(PLArenaPool * poolp,NSSCMSAttribute *** attrs,NSSCMSAttribute * attr)438 static SECStatus my_NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr)
439 {
440 NSSCMSAttribute *oattr;
441 void *mark;
442 SECOidTag type;
443
444 mark = PORT_ArenaMark(poolp);
445
446 /* find oidtag of attr */
447 type = my_NSS_CMSAttribute_GetType(attr);
448
449 /* see if we have one already */
450 oattr = my_NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
451 PORT_Assert(oattr == NULL);
452 if (oattr != nullptr)
453 goto loser; /* XXX or would it be better to replace it? */
454
455 /* no, shove it in */
456 if (my_NSS_CMSArray_Add(poolp, reinterpret_cast<void ***>(attrs), static_cast<void *>(attr)) != SECSuccess)
457 goto loser;
458
459 PORT_ArenaUnmark(poolp, mark);
460 return SECSuccess;
461
462 loser:
463 PORT_ArenaRelease(poolp, mark);
464 return SECFailure;
465 }
466
my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo * signerinfo,NSSCMSAttribute * attr)467 static SECStatus my_NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
468 {
469 return my_NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
470 }
471
digestLength(SECOidTag digestAlgId)472 unsigned int SignatureHandler::digestLength(SECOidTag digestAlgId)
473 {
474 switch (digestAlgId) {
475 case SEC_OID_SHA1:
476 return 20;
477 case SEC_OID_SHA256:
478 return 32;
479 case SEC_OID_SHA384:
480 return 48;
481 case SEC_OID_SHA512:
482 return 64;
483 default:
484 printf("ERROR: Unrecognized Hash ID\n");
485 return 0;
486 }
487 }
488
getHashOidTag(const char * digestName)489 SECOidTag SignatureHandler::getHashOidTag(const char *digestName)
490 {
491 SECOidTag tag = SEC_OID_UNKNOWN;
492 if (strcmp(digestName, "SHA1") == 0) {
493 tag = SEC_OID_SHA1;
494 } else if (strcmp(digestName, "SHA256") == 0) {
495 tag = SEC_OID_SHA256;
496 } else if (strcmp(digestName, "SHA384") == 0) {
497 tag = SEC_OID_SHA384;
498 } else if (strcmp(digestName, "SHA512") == 0) {
499 tag = SEC_OID_SHA512;
500 }
501 return tag;
502 }
503
getSignerName()504 char *SignatureHandler::getSignerName()
505 {
506 char *commonName, *name;
507
508 if (!CMSSignerInfo || !NSS_IsInitialized())
509 return nullptr;
510
511 if (!signing_cert)
512 signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB());
513
514 if (!signing_cert)
515 return nullptr;
516
517 commonName = CERT_GetCommonName(&signing_cert->subject);
518 name = strdup(commonName);
519 PORT_Free(commonName);
520
521 return name;
522 }
523
getSignerSubjectDN()524 const char *SignatureHandler::getSignerSubjectDN()
525 {
526 if (!signing_cert && !CMSSignerInfo)
527 return nullptr;
528
529 if (!signing_cert)
530 signing_cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB());
531
532 if (!signing_cert)
533 return nullptr;
534
535 return signing_cert->subjectName;
536 }
537
getHashAlgorithm()538 HASH_HashType SignatureHandler::getHashAlgorithm()
539 {
540 if (hash_context && hash_context->hashobj) {
541 return hash_context->hashobj->type;
542 }
543 return HASH_AlgNULL;
544 }
545
getSigningTime()546 time_t SignatureHandler::getSigningTime()
547 {
548 PRTime sTime; // time in microseconds since the epoch
549
550 if (NSS_CMSSignerInfo_GetSigningTime(CMSSignerInfo, &sTime) != SECSuccess)
551 return 0;
552
553 return static_cast<time_t>(sTime / 1000000);
554 }
555
getEntityInfo(CERTName * entityName)556 static X509CertificateInfo::EntityInfo getEntityInfo(CERTName *entityName)
557 {
558 X509CertificateInfo::EntityInfo info;
559
560 if (!entityName)
561 return info;
562
563 char *dn = CERT_NameToAscii(entityName);
564 if (dn) {
565 info.distinguishedName = dn;
566 PORT_Free(dn);
567 }
568
569 char *cn = CERT_GetCommonName(entityName);
570 if (cn) {
571 info.commonName = cn;
572 PORT_Free(cn);
573 }
574
575 char *email = CERT_GetCertEmailAddress(entityName);
576 if (email) {
577 info.email = email;
578 PORT_Free(email);
579 }
580
581 char *org = CERT_GetOrgName(entityName);
582 if (org) {
583 info.organization = org;
584 PORT_Free(org);
585 }
586
587 return info;
588 }
589
SECItemToGooString(const SECItem & secItem)590 static GooString SECItemToGooString(const SECItem &secItem)
591 {
592 // TODO do we need to handle secItem.type;
593 return GooString((const char *)secItem.data, secItem.len);
594 }
595
getCertificateInfoFromCERT(CERTCertificate * cert)596 static std::unique_ptr<X509CertificateInfo> getCertificateInfoFromCERT(CERTCertificate *cert)
597 {
598 auto certInfo = std::make_unique<X509CertificateInfo>();
599
600 certInfo->setVersion(DER_GetInteger(&cert->version) + 1);
601 certInfo->setSerialNumber(SECItemToGooString(cert->serialNumber));
602
603 // issuer info
604 certInfo->setIssuerInfo(getEntityInfo(&cert->issuer));
605
606 // validity
607 PRTime notBefore, notAfter;
608 CERT_GetCertTimes(cert, ¬Before, ¬After);
609 X509CertificateInfo::Validity certValidity;
610 certValidity.notBefore = static_cast<time_t>(notBefore / 1000000);
611 certValidity.notAfter = static_cast<time_t>(notAfter / 1000000);
612 certInfo->setValidity(certValidity);
613
614 // subject info
615 certInfo->setSubjectInfo(getEntityInfo(&cert->subject));
616
617 // nickname (as a handle to refer to the CERT later)
618 certInfo->setNickName(GooString(cert->dbnickname));
619
620 // public key info
621 X509CertificateInfo::PublicKeyInfo pkInfo;
622 SECKEYPublicKey *pk = CERT_ExtractPublicKey(cert);
623 switch (pk->keyType) {
624 case rsaKey:
625 pkInfo.publicKey = SECItemToGooString(pk->u.rsa.modulus);
626 pkInfo.publicKeyType = RSAKEY;
627 break;
628 case dsaKey:
629 pkInfo.publicKey = SECItemToGooString(pk->u.dsa.publicValue);
630 pkInfo.publicKeyType = DSAKEY;
631 break;
632 case ecKey:
633 pkInfo.publicKey = SECItemToGooString(pk->u.ec.publicValue);
634 pkInfo.publicKeyType = ECKEY;
635 break;
636 default:
637 pkInfo.publicKey = SECItemToGooString(cert->subjectPublicKeyInfo.subjectPublicKey);
638 pkInfo.publicKeyType = OTHERKEY;
639 break;
640 }
641 pkInfo.publicKeyStrength = SECKEY_PublicKeyStrengthInBits(pk);
642 certInfo->setPublicKeyInfo(std::move(pkInfo));
643
644 certInfo->setKeyUsageExtensions(cert->keyUsage);
645 certInfo->setCertificateDER(SECItemToGooString(cert->derCert));
646 certInfo->setIsSelfSigned(CERT_CompareName(&cert->subject, &cert->issuer) == SECEqual);
647
648 SECKEY_DestroyPublicKey(pk);
649
650 return certInfo;
651 }
652
getCertificateInfo() const653 std::unique_ptr<X509CertificateInfo> SignatureHandler::getCertificateInfo() const
654 {
655 if (CMSSignerInfo) {
656 CERTCertificate *cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB());
657 if (!cert)
658 return nullptr;
659 return getCertificateInfoFromCERT(cert);
660 } else {
661 if (!signing_cert)
662 return nullptr;
663
664 return getCertificateInfoFromCERT(signing_cert);
665 }
666 }
667
getDefaultFirefoxCertDB_Linux()668 static GooString *getDefaultFirefoxCertDB_Linux()
669 {
670 GooString *finalPath = nullptr;
671 DIR *toSearchIn;
672 struct dirent *subFolder;
673
674 GooString *homePath = new GooString(getenv("HOME"));
675 homePath = homePath->append("/.mozilla/firefox/");
676
677 if ((toSearchIn = opendir(homePath->c_str())) == nullptr) {
678 error(errInternal, 0, "couldn't find default Firefox Folder");
679 delete homePath;
680 return nullptr;
681 }
682 do {
683 if ((subFolder = readdir(toSearchIn)) != nullptr) {
684 if (strstr(subFolder->d_name, "default") != nullptr) {
685 finalPath = homePath->append(subFolder->d_name);
686 closedir(toSearchIn);
687 return finalPath;
688 }
689 }
690 } while (subFolder != nullptr);
691
692 closedir(toSearchIn);
693 delete homePath;
694 return nullptr;
695 }
696
697 std::string SignatureHandler::sNssDir;
698
699 /**
700 * Initialise NSS
701 */
setNSSDir(const GooString & nssDir)702 void SignatureHandler::setNSSDir(const GooString &nssDir)
703 {
704 static bool setNssDirCalled = false;
705
706 if (NSS_IsInitialized() && nssDir.getLength() > 0) {
707 error(errInternal, 0, "You need to call setNSSDir before signature validation related operations happen");
708 return;
709 }
710
711 if (setNssDirCalled)
712 return;
713
714 setNssDirCalled = true;
715
716 atexit(shutdownNss);
717
718 bool initSuccess = false;
719 if (nssDir.getLength() > 0) {
720 initSuccess = (NSS_Init(nssDir.c_str()) == SECSuccess);
721 sNssDir = nssDir.toStr();
722 } else {
723 GooString *certDBPath = getDefaultFirefoxCertDB_Linux();
724 if (certDBPath == nullptr) {
725 initSuccess = (NSS_Init("sql:/etc/pki/nssdb") == SECSuccess);
726 sNssDir = "sql:/etc/pki/nssdb";
727 } else {
728 initSuccess = (NSS_Init(certDBPath->c_str()) == SECSuccess);
729 sNssDir = certDBPath->toStr();
730 }
731 if (!initSuccess) {
732 GooString homeNssDb(getenv("HOME"));
733 homeNssDb.append("/.pki/nssdb");
734 initSuccess = (NSS_Init(homeNssDb.c_str()) == SECSuccess);
735 sNssDir = homeNssDb.toStr();
736 if (!initSuccess) {
737 NSS_NoDB_Init(nullptr);
738 }
739 }
740 delete certDBPath;
741 }
742
743 if (initSuccess) {
744 // Make sure NSS root certificates module is loaded
745 SECMOD_AddNewModule("Root Certs", "libnssckbi.so", 0, 0);
746 }
747 }
748
getNSSDir()749 std::string SignatureHandler::getNSSDir()
750 {
751 return sNssDir;
752 }
753
754 static std::function<char *(const char *)> PasswordFunction;
755
setNSSPasswordCallback(const std::function<char * (const char *)> & f)756 void SignatureHandler::setNSSPasswordCallback(const std::function<char *(const char *)> &f)
757 {
758 PasswordFunction = f;
759 }
760
SignatureHandler(unsigned char * p7,int p7_length)761 SignatureHandler::SignatureHandler(unsigned char *p7, int p7_length) : hash_context(nullptr), CMSMessage(nullptr), CMSSignedData(nullptr), CMSSignerInfo(nullptr), signing_cert(nullptr), temp_certs(nullptr)
762 {
763 setNSSDir({});
764 CMSitem.data = p7;
765 CMSitem.len = p7_length;
766 CMSMessage = CMS_MessageCreate(&CMSitem);
767 CMSSignedData = CMS_SignedDataCreate(CMSMessage);
768 if (CMSSignedData) {
769 CMSSignerInfo = CMS_SignerInfoCreate(CMSSignedData);
770 hash_context = initHashContext();
771 }
772 }
773
SignatureHandler(const char * certNickname,SECOidTag digestAlgTag)774 SignatureHandler::SignatureHandler(const char *certNickname, SECOidTag digestAlgTag)
775 : hash_length(digestLength(digestAlgTag)), digest_alg_tag(digestAlgTag), CMSitem(), hash_context(nullptr), CMSMessage(nullptr), CMSSignedData(nullptr), CMSSignerInfo(nullptr), signing_cert(nullptr), temp_certs(nullptr)
776 {
777 setNSSDir({});
778 CMSMessage = NSS_CMSMessage_Create(nullptr);
779 signing_cert = CERT_FindCertByNickname(CERT_GetDefaultCertDB(), certNickname);
780 hash_context = HASH_Create(HASH_GetHashTypeByOidTag(digestAlgTag));
781 }
782
SignatureHandler()783 SignatureHandler::SignatureHandler() : hash_length(), digest_alg_tag(), CMSitem(), hash_context(nullptr), CMSMessage(nullptr), CMSSignedData(nullptr), CMSSignerInfo(nullptr), signing_cert(nullptr), temp_certs(nullptr)
784 {
785 setNSSDir({});
786 CMSMessage = NSS_CMSMessage_Create(nullptr);
787 }
788
initHashContext()789 HASHContext *SignatureHandler::initHashContext()
790 {
791
792 SECItem usedAlgorithm = NSS_CMSSignedData_GetDigestAlgs(CMSSignedData)[0]->algorithm;
793 hash_length = digestLength(SECOID_FindOIDTag(&usedAlgorithm));
794 HASH_HashType hashType;
795 hashType = HASH_GetHashTypeByOidTag(SECOID_FindOIDTag(&usedAlgorithm));
796 return HASH_Create(hashType);
797 }
798
updateHash(unsigned char * data_block,int data_len)799 void SignatureHandler::updateHash(unsigned char *data_block, int data_len)
800 {
801 if (hash_context) {
802 HASH_Update(hash_context, data_block, data_len);
803 }
804 }
805
restartHash()806 void SignatureHandler::restartHash()
807 {
808 if (hash_context)
809 HASH_Destroy(hash_context);
810 hash_context = HASH_Create(HASH_GetHashTypeByOidTag(digest_alg_tag));
811 }
812
~SignatureHandler()813 SignatureHandler::~SignatureHandler()
814 {
815 SECITEM_FreeItem(&CMSitem, PR_FALSE);
816 if (CMSMessage)
817 NSS_CMSMessage_Destroy(CMSMessage);
818
819 if (hash_context)
820 HASH_Destroy(hash_context);
821
822 free(temp_certs);
823 }
824
CMS_MessageCreate(SECItem * cms_item)825 NSSCMSMessage *SignatureHandler::CMS_MessageCreate(SECItem *cms_item)
826 {
827 if (cms_item->data) {
828 return NSS_CMSMessage_CreateFromDER(cms_item, nullptr, nullptr /* Content callback */
829 ,
830 nullptr, nullptr /*Password callback*/
831 ,
832 nullptr, nullptr /*Decrypt callback*/);
833 } else {
834 return nullptr;
835 }
836 }
837
CMS_SignedDataCreate(NSSCMSMessage * cms_msg)838 NSSCMSSignedData *SignatureHandler::CMS_SignedDataCreate(NSSCMSMessage *cms_msg)
839 {
840 if (!NSS_CMSMessage_IsSigned(cms_msg)) {
841 error(errInternal, 0, "Input couldn't be parsed as a CMS signature");
842 return nullptr;
843 }
844
845 NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(cms_msg, 0);
846 if (!cinfo) {
847 error(errInternal, 0, "Error in NSS_CMSMessage_ContentLevel");
848 return nullptr;
849 }
850
851 NSSCMSSignedData *signedData = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
852 if (!signedData) {
853 error(errInternal, 0, "CError in NSS_CMSContentInfo_GetContent()");
854 return nullptr;
855 }
856
857 if (signedData->rawCerts) {
858 size_t i;
859 for (i = 0; signedData->rawCerts[i]; ++i) { } // just count the length of the certificate chain
860
861 // tempCerts field needs to be filled for complete memory release by NSSCMSSignedData_Destroy
862 signedData->tempCerts = (CERTCertificate **)gmallocn(i + 1, sizeof(CERTCertificate *));
863 memset(signedData->tempCerts, 0, (i + 1) * sizeof(CERTCertificate *));
864 // store the addresses of these temporary certificates for future release
865 for (i = 0; signedData->rawCerts[i]; ++i)
866 signedData->tempCerts[i] = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), signedData->rawCerts[i], nullptr, 0, 0);
867
868 temp_certs = signedData->tempCerts;
869 return signedData;
870 } else {
871 return nullptr;
872 }
873 }
874
CMS_SignerInfoCreate(NSSCMSSignedData * cms_sig_data)875 NSSCMSSignerInfo *SignatureHandler::CMS_SignerInfoCreate(NSSCMSSignedData *cms_sig_data)
876 {
877 NSSCMSSignerInfo *signerInfo = NSS_CMSSignedData_GetSignerInfo(cms_sig_data, 0);
878 if (!signerInfo) {
879 printf("Error in NSS_CMSSignedData_GetSignerInfo()\n");
880 return nullptr;
881 } else {
882 return signerInfo;
883 }
884 }
885
NSS_SigTranslate(NSSCMSVerificationStatus nss_code)886 static SignatureValidationStatus NSS_SigTranslate(NSSCMSVerificationStatus nss_code)
887 {
888 switch (nss_code) {
889 case NSSCMSVS_GoodSignature:
890 return SIGNATURE_VALID;
891
892 case NSSCMSVS_BadSignature:
893 return SIGNATURE_INVALID;
894
895 case NSSCMSVS_DigestMismatch:
896 return SIGNATURE_DIGEST_MISMATCH;
897
898 case NSSCMSVS_ProcessingError:
899 return SIGNATURE_DECODING_ERROR;
900
901 default:
902 return SIGNATURE_GENERIC_ERROR;
903 }
904 }
905
validateSignature()906 SignatureValidationStatus SignatureHandler::validateSignature()
907 {
908 unsigned char *digest_buffer = nullptr;
909
910 if (!CMSSignedData)
911 return SIGNATURE_GENERIC_ERROR;
912
913 if (!NSS_IsInitialized())
914 return SIGNATURE_GENERIC_ERROR;
915
916 if (!hash_context)
917 return SIGNATURE_GENERIC_ERROR;
918
919 digest_buffer = (unsigned char *)PORT_Alloc(hash_length);
920 unsigned int result_len = 0;
921
922 HASH_End(hash_context, digest_buffer, &result_len, hash_length);
923
924 SECItem digest;
925 digest.data = digest_buffer;
926 digest.len = hash_length;
927
928 if ((NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr)
929 CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
930
931 SECItem *content_info_data = CMSSignedData->contentInfo.content.data;
932 if (content_info_data != nullptr && content_info_data->data != nullptr) {
933 /*
934 This means it's not a detached type signature
935 so the digest is contained in SignedData->contentInfo
936 */
937 if (memcmp(digest.data, content_info_data->data, hash_length) == 0 && digest.len == content_info_data->len) {
938 PORT_Free(digest_buffer);
939 return SIGNATURE_VALID;
940 } else {
941 PORT_Free(digest_buffer);
942 return SIGNATURE_DIGEST_MISMATCH;
943 }
944
945 } else if (NSS_CMSSignerInfo_Verify(CMSSignerInfo, &digest, nullptr) != SECSuccess) {
946
947 PORT_Free(digest_buffer);
948 return NSS_SigTranslate(CMSSignerInfo->verificationStatus);
949 } else {
950 PORT_Free(digest_buffer);
951 return SIGNATURE_VALID;
952 }
953 }
954
validateCertificate(time_t validation_time,bool ocspRevocationCheck,bool useAIACertFetch)955 CertificateValidationStatus SignatureHandler::validateCertificate(time_t validation_time, bool ocspRevocationCheck, bool useAIACertFetch)
956 {
957 CERTCertificate *cert;
958
959 if (!CMSSignerInfo)
960 return CERTIFICATE_GENERIC_ERROR;
961
962 if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(CMSSignerInfo, CERT_GetDefaultCertDB())) == nullptr)
963 CMSSignerInfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
964
965 PRTime vTime = 0; // time in microseconds since the epoch, special value 0 means now
966 if (validation_time > 0)
967 vTime = 1000000 * (PRTime)validation_time;
968 CERTValInParam inParams[4];
969 inParams[0].type = cert_pi_revocationFlags;
970 if (ocspRevocationCheck) {
971 inParams[0].value.pointer.revocation = CERT_GetClassicOCSPEnabledSoftFailurePolicy();
972 } else {
973 inParams[0].value.pointer.revocation = CERT_GetClassicOCSPDisabledPolicy();
974 }
975 inParams[1].type = cert_pi_date;
976 inParams[1].value.scalar.time = vTime;
977 if (useAIACertFetch) {
978 inParams[2].type = cert_pi_useAIACertFetch;
979 inParams[2].value.scalar.b = PR_TRUE;
980 inParams[3].type = cert_pi_end;
981 } else {
982 inParams[2].type = cert_pi_end;
983 }
984
985 CERT_PKIXVerifyCert(cert, certificateUsageEmailSigner, inParams, nullptr, CMSSignerInfo->cmsg->pwfn_arg);
986
987 switch (PORT_GetError()) {
988 // 0 not defined in SECErrorCodes, it means success for this purpose.
989 case 0:
990 return CERTIFICATE_TRUSTED;
991
992 case SEC_ERROR_UNKNOWN_ISSUER:
993 return CERTIFICATE_UNKNOWN_ISSUER;
994
995 case SEC_ERROR_UNTRUSTED_ISSUER:
996 return CERTIFICATE_UNTRUSTED_ISSUER;
997
998 case SEC_ERROR_REVOKED_CERTIFICATE:
999 return CERTIFICATE_REVOKED;
1000
1001 case SEC_ERROR_EXPIRED_CERTIFICATE:
1002 return CERTIFICATE_EXPIRED;
1003 }
1004
1005 return CERTIFICATE_GENERIC_ERROR;
1006 }
1007
signDetached(const char * password) const1008 std::unique_ptr<GooString> SignatureHandler::signDetached(const char *password) const
1009 {
1010 if (!hash_context)
1011 return nullptr;
1012 unsigned char *digest_buffer = reinterpret_cast<unsigned char *>(PORT_Alloc(hash_length));
1013 unsigned int result_len = 0;
1014 HASH_End(hash_context, digest_buffer, &result_len, hash_length);
1015 SECItem digest;
1016 digest.data = digest_buffer;
1017 digest.len = result_len;
1018
1019 /////////////////////////////////////
1020 /// Code from LibreOffice under MPLv2
1021 /////////////////////////////////////
1022
1023 NSSCMSMessage *cms_msg = NSS_CMSMessage_Create(nullptr);
1024 if (!cms_msg)
1025 return nullptr;
1026
1027 NSSCMSSignedData *cms_sd = NSS_CMSSignedData_Create(cms_msg);
1028 if (!cms_sd)
1029 return nullptr;
1030
1031 NSSCMSContentInfo *cms_cinfo = NSS_CMSMessage_GetContentInfo(cms_msg);
1032
1033 if (NSS_CMSContentInfo_SetContent_SignedData(cms_msg, cms_cinfo, cms_sd) != SECSuccess)
1034 return nullptr;
1035
1036 cms_cinfo = NSS_CMSSignedData_GetContentInfo(cms_sd);
1037
1038 // Attach NULL data as detached data
1039 if (NSS_CMSContentInfo_SetContent_Data(cms_msg, cms_cinfo, nullptr, PR_TRUE) != SECSuccess)
1040 return nullptr;
1041
1042 // hardcode SHA256 these days...
1043 NSSCMSSignerInfo *cms_signer = NSS_CMSSignerInfo_Create(cms_msg, signing_cert, SEC_OID_SHA256);
1044 if (!cms_signer)
1045 return nullptr;
1046
1047 if (NSS_CMSSignerInfo_IncludeCerts(cms_signer, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess)
1048 return nullptr;
1049
1050 if (NSS_CMSSignedData_AddCertificate(cms_sd, signing_cert) != SECSuccess)
1051 return nullptr;
1052
1053 if (NSS_CMSSignedData_AddSignerInfo(cms_sd, cms_signer) != SECSuccess)
1054 return nullptr;
1055
1056 if (NSS_CMSSignedData_SetDigestValue(cms_sd, SEC_OID_SHA256, &digest) != SECSuccess)
1057 return nullptr;
1058
1059 // Add the signing certificate as a signed attribute.
1060 ESSCertIDv2 *aCertIDs[2];
1061 ESSCertIDv2 aCertID;
1062 // Write ESSCertIDv2.hashAlgorithm.
1063 aCertID.hashAlgorithm.algorithm.data = nullptr;
1064 aCertID.hashAlgorithm.parameters.data = nullptr;
1065 SECOID_SetAlgorithmID(nullptr, &aCertID.hashAlgorithm, SEC_OID_SHA256, nullptr);
1066
1067 // Write ESSCertIDv2.certHash.
1068 SECItem aCertHashItem;
1069 unsigned char certhash[32];
1070 SECStatus rv = PK11_HashBuf(SEC_OID_SHA256, certhash, signing_cert->derCert.data, signing_cert->derCert.len);
1071 if (rv != SECSuccess)
1072 return nullptr;
1073
1074 aCertHashItem.type = siBuffer;
1075 aCertHashItem.data = certhash;
1076 aCertHashItem.len = 32;
1077 aCertID.certHash = aCertHashItem;
1078
1079 // Write ESSCertIDv2.issuerSerial.
1080 IssuerSerial aSerial;
1081 GeneralName aName;
1082 aName.name = signing_cert->issuer;
1083 aSerial.issuer.names = aName;
1084 aSerial.serialNumber = signing_cert->serialNumber;
1085 aCertID.issuerSerial = aSerial;
1086 // Write SigningCertificateV2.certs.
1087 aCertIDs[0] = &aCertID;
1088 aCertIDs[1] = nullptr;
1089 SigningCertificateV2 aCertificate;
1090 aCertificate.certs = &aCertIDs[0];
1091
1092 SECItem *pEncodedCertificate = SEC_ASN1EncodeItem(nullptr, nullptr, &aCertificate, SigningCertificateV2Template);
1093 if (!pEncodedCertificate)
1094 return nullptr;
1095
1096 NSSCMSAttribute aAttribute;
1097 SECItem aAttributeValues[2];
1098 SECItem *pAttributeValues[2];
1099 pAttributeValues[0] = aAttributeValues;
1100 pAttributeValues[1] = nullptr;
1101 aAttributeValues[0] = *pEncodedCertificate;
1102 aAttributeValues[1].type = siBuffer;
1103 aAttributeValues[1].data = nullptr;
1104 aAttributeValues[1].len = 0;
1105 aAttribute.values = pAttributeValues;
1106
1107 SECOidData aOidData;
1108 aOidData.oid.data = nullptr;
1109 /*
1110 * id-aa-signingCertificateV2 OBJECT IDENTIFIER ::=
1111 * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9)
1112 * smime(16) id-aa(2) 47 }
1113 */
1114 if (my_SEC_StringToOID(&aOidData.oid, "1.2.840.113549.1.9.16.2.47", 0) != SECSuccess)
1115 return nullptr;
1116
1117 aOidData.offset = SEC_OID_UNKNOWN;
1118 aOidData.desc = "id-aa-signingCertificateV2";
1119 aOidData.mechanism = CKM_SHA_1;
1120 aOidData.supportedExtension = UNSUPPORTED_CERT_EXTENSION;
1121 aAttribute.typeTag = &aOidData;
1122 aAttribute.type = aOidData.oid;
1123 aAttribute.encoded = PR_TRUE;
1124
1125 if (my_NSS_CMSSignerInfo_AddAuthAttr(cms_signer, &aAttribute) != SECSuccess)
1126 return nullptr;
1127
1128 SECItem cms_output;
1129 cms_output.data = nullptr;
1130 cms_output.len = 0;
1131 PLArenaPool *arena = PORT_NewArena(10000);
1132
1133 NSSCMSEncoderContext *cms_ecx = NSS_CMSEncoder_Start(cms_msg, nullptr, nullptr, &cms_output, arena, passwordCallback, const_cast<char *>(password), nullptr, nullptr, nullptr, nullptr);
1134 if (!cms_ecx) {
1135 PORT_FreeArena(arena, PR_FALSE);
1136 return nullptr;
1137 }
1138
1139 if (NSS_CMSEncoder_Finish(cms_ecx) != SECSuccess) {
1140 PORT_FreeArena(arena, PR_FALSE);
1141 return nullptr;
1142 }
1143
1144 GooString *signature = new GooString(reinterpret_cast<const char *>(cms_output.data), cms_output.len);
1145
1146 SECITEM_FreeItem(pEncodedCertificate, PR_TRUE);
1147 NSS_CMSMessage_Destroy(cms_msg);
1148 PORT_FreeArena(arena, PR_FALSE);
1149
1150 return std::unique_ptr<GooString>(signature);
1151 }
1152
GetPasswordFunction(PK11SlotInfo * slot,PRBool,void *)1153 static char *GetPasswordFunction(PK11SlotInfo *slot, PRBool /*retry*/, void * /*arg*/)
1154 {
1155 const char *name = PK11_GetTokenName(slot);
1156 if (PasswordFunction) {
1157 return PasswordFunction(name);
1158 }
1159 return nullptr;
1160 }
1161
getAvailableSigningCertificates()1162 std::vector<std::unique_ptr<X509CertificateInfo>> SignatureHandler::getAvailableSigningCertificates()
1163 {
1164 // set callback, in case one of the slots has a password set
1165 PK11_SetPasswordFunc(GetPasswordFunction);
1166 setNSSDir({});
1167
1168 std::vector<std::unique_ptr<X509CertificateInfo>> certsList;
1169 PK11SlotList *slotList = PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_FALSE, nullptr);
1170 if (slotList) {
1171 for (PK11SlotListElement *slotElement = slotList->head; slotElement; slotElement = slotElement->next) {
1172 PK11SlotInfo *pSlot = slotElement->slot;
1173 if (PK11_NeedLogin(pSlot)) {
1174 SECStatus nRet = PK11_Authenticate(pSlot, PR_TRUE, nullptr);
1175 // PK11_Authenticate may fail in case the a slot has not been initialized.
1176 // this is the case if the user has a new profile, so that they have never
1177 // added a personal certificate.
1178 if (nRet != SECSuccess && PORT_GetError() != SEC_ERROR_IO)
1179 continue;
1180 }
1181
1182 SECKEYPrivateKeyList *privKeyList = PK11_ListPrivateKeysInSlot(pSlot);
1183 if (privKeyList) {
1184 for (SECKEYPrivateKeyListNode *curPri = PRIVKEY_LIST_HEAD(privKeyList); !PRIVKEY_LIST_END(curPri, privKeyList) && curPri != nullptr; curPri = PRIVKEY_LIST_NEXT(curPri)) {
1185 if (curPri->key) {
1186 CERTCertificate *cert = PK11_GetCertFromPrivateKey(curPri->key);
1187 if (cert) {
1188 certsList.push_back(getCertificateInfoFromCERT(cert));
1189 CERT_DestroyCertificate(cert);
1190 }
1191 }
1192 }
1193 SECKEY_DestroyPrivateKeyList(privKeyList);
1194 }
1195 }
1196 PK11_FreeSlotList(slotList);
1197 }
1198
1199 PK11_SetPasswordFunc(nullptr);
1200
1201 return certsList;
1202 }
1203