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

source code of poppler/poppler/NSSCryptoSignBackend.cc