1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 /*
6 * CMS signerInfo methods.
7 */
8
9 #include "cmslocal.h"
10
11 #include "cert.h"
12 #include "keyhi.h"
13 #include "secasn1.h"
14 #include "secitem.h"
15 #include "secoid.h"
16 #include "pk11func.h"
17 #include "prtime.h"
18 #include "secerr.h"
19 #include "secder.h"
20 #include "cryptohi.h"
21
22 #include "smime.h"
23
24 /* =============================================================================
25 * SIGNERINFO
26 */
27 NSSCMSSignerInfo *
28 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
29 CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
30 SECKEYPrivateKey *signingKey, SECOidTag digestalgtag);
31
32 NSSCMSSignerInfo *
NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage * cmsg,SECItem * subjKeyID,SECKEYPublicKey * pubKey,SECKEYPrivateKey * signingKey,SECOidTag digestalgtag)33 NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID,
34 SECKEYPublicKey *pubKey,
35 SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
36 {
37 return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL,
38 subjKeyID, pubKey, signingKey, digestalgtag);
39 }
40
41 NSSCMSSignerInfo *
NSS_CMSSignerInfo_Create(NSSCMSMessage * cmsg,CERTCertificate * cert,SECOidTag digestalgtag)42 NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
43 {
44 return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL,
45 NULL, NULL, digestalgtag);
46 }
47
48 NSSCMSSignerInfo *
nss_cmssignerinfo_create(NSSCMSMessage * cmsg,NSSCMSSignerIDSelector type,CERTCertificate * cert,SECItem * subjKeyID,SECKEYPublicKey * pubKey,SECKEYPrivateKey * signingKey,SECOidTag digestalgtag)49 nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type,
50 CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey,
51 SECKEYPrivateKey *signingKey, SECOidTag digestalgtag)
52 {
53 void *mark;
54 NSSCMSSignerInfo *signerinfo;
55 int version;
56 PLArenaPool *poolp;
57 SECStatus rv;
58
59 poolp = cmsg->poolp;
60
61 mark = PORT_ArenaMark(poolp);
62
63 signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
64 if (signerinfo == NULL) {
65 PORT_ArenaRelease(poolp, mark);
66 return NULL;
67 }
68
69 signerinfo->cmsg = cmsg;
70
71 switch (type) {
72 case NSSCMSSignerID_IssuerSN:
73 signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN;
74 if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
75 goto loser;
76 if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
77 goto loser;
78 break;
79 case NSSCMSSignerID_SubjectKeyID:
80 signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID;
81 PORT_Assert(subjKeyID);
82 if (!subjKeyID)
83 goto loser;
84
85 signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem);
86 rv = SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID,
87 subjKeyID);
88 if (rv != SECSuccess) {
89 goto loser;
90 }
91 signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey);
92 if (!signerinfo->signingKey)
93 goto loser;
94 signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey);
95 if (!signerinfo->pubKey)
96 goto loser;
97 break;
98 default:
99 goto loser;
100 }
101
102 /* set version right now */
103 version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
104 /* RFC2630 5.3 "version is the syntax version number. If the .... " */
105 if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
106 version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
107 (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
108
109 if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
110 goto loser;
111
112 PORT_ArenaUnmark(poolp, mark);
113 return signerinfo;
114
115 loser:
116 PORT_ArenaRelease(poolp, mark);
117 return NULL;
118 }
119
120 /*
121 * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
122 */
123 void
NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo * si)124 NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
125 {
126 if (si->cert != NULL)
127 CERT_DestroyCertificate(si->cert);
128
129 if (si->certList != NULL)
130 CERT_DestroyCertificateList(si->certList);
131
132 /* XXX storage ??? */
133 }
134 static SECOidTag
NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(KeyType keyType,SECOidTag pubkAlgTag,SECOidTag signAlgTag)135 NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(KeyType keyType,
136 SECOidTag pubkAlgTag,
137 SECOidTag signAlgTag)
138 {
139 switch (keyType) {
140 case rsaKey:
141 return pubkAlgTag;
142 case rsaPssKey:
143 case dsaKey:
144 case ecKey:
145 return signAlgTag;
146 default:
147 return SEC_OID_UNKNOWN;
148 }
149 }
150
151 /*
152 * NSS_CMSSignerInfo_Sign - sign something
153 *
154 */
155 SECStatus
NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo * signerinfo,SECItem * digest,SECItem * contentType)156 NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest,
157 SECItem *contentType)
158 {
159 CERTCertificate *cert;
160 SECKEYPrivateKey *privkey = NULL;
161 SECOidTag digestalgtag;
162 SECOidTag pubkAlgTag;
163 SECOidTag signAlgTag;
164 SECOidTag cmsSignAlgTag;
165 SECItem signature = { 0 };
166 SECStatus rv;
167 PLArenaPool *poolp, *tmppoolp = NULL;
168 SECAlgorithmID *algID, freeAlgID;
169 CERTSubjectPublicKeyInfo *spki;
170
171 PORT_Assert(digest != NULL);
172
173 poolp = signerinfo->cmsg->poolp;
174
175 switch (signerinfo->signerIdentifier.identifierType) {
176 case NSSCMSSignerID_IssuerSN:
177 cert = signerinfo->cert;
178
179 privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg);
180 if (privkey == NULL)
181 goto loser;
182 algID = &cert->subjectPublicKeyInfo.algorithm;
183 break;
184 case NSSCMSSignerID_SubjectKeyID:
185 privkey = signerinfo->signingKey;
186 signerinfo->signingKey = NULL;
187 spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
188 SECKEY_DestroyPublicKey(signerinfo->pubKey);
189 signerinfo->pubKey = NULL;
190 SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
191 SECKEY_DestroySubjectPublicKeyInfo(spki);
192 algID = &freeAlgID;
193 break;
194 default:
195 goto loser;
196 }
197 digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
198 /*
199 * XXX I think there should be a cert-level interface for this,
200 * so that I do not have to know about subjectPublicKeyInfo...
201 */
202 pubkAlgTag = SECOID_GetAlgorithmTag(algID);
203 if (algID == &freeAlgID) {
204 SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
205 }
206
207 signAlgTag = SEC_GetSignatureAlgorithmOidTag(SECKEY_GetPrivateKeyType(privkey),
208 digestalgtag);
209 if (signAlgTag == SEC_OID_UNKNOWN) {
210 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
211 goto loser;
212 }
213
214 cmsSignAlgTag = NSS_CMSSignerInfo_GetSignatureAlgorithmOidTag(
215 SECKEY_GetPrivateKeyType(privkey), pubkAlgTag, signAlgTag);
216 if (cmsSignAlgTag == SEC_OID_UNKNOWN) {
217 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
218 goto loser;
219 }
220
221 if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg),
222 cmsSignAlgTag, NULL) != SECSuccess)
223 goto loser;
224
225 if (signerinfo->authAttr != NULL) {
226 SECItem encoded_attrs;
227
228 /* find and fill in the message digest attribute. */
229 rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
230 SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
231 if (rv != SECSuccess)
232 goto loser;
233
234 if (contentType != NULL) {
235 /* if the caller wants us to, find and fill in the content type attribute. */
236 rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr),
237 SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
238 if (rv != SECSuccess)
239 goto loser;
240 }
241
242 if ((tmppoolp = PORT_NewArena(1024)) == NULL) {
243 PORT_SetError(SEC_ERROR_NO_MEMORY);
244 goto loser;
245 }
246
247 /*
248 * Before encoding, reorder the attributes so that when they
249 * are encoded, they will be conforming DER, which is required
250 * to have a specific order and that is what must be used for
251 * the hash/signature. We do this here, rather than building
252 * it into EncodeAttributes, because we do not want to do
253 * such reordering on incoming messages (which also uses
254 * EncodeAttributes) or our old signatures (and other "broken"
255 * implementations) will not verify. So, we want to guarantee
256 * that we send out good DER encodings of attributes, but not
257 * to expect to receive them.
258 */
259 if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
260 goto loser;
261
262 encoded_attrs.data = NULL;
263 encoded_attrs.len = 0;
264 if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr),
265 &encoded_attrs) == NULL)
266 goto loser;
267
268 rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len,
269 privkey, signAlgTag);
270 PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
271 tmppoolp = 0;
272 } else {
273 rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
274 }
275 SECKEY_DestroyPrivateKey(privkey);
276 privkey = NULL;
277
278 if (rv != SECSuccess)
279 goto loser;
280
281 if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess)
282 goto loser;
283
284 SECITEM_FreeItem(&signature, PR_FALSE);
285
286 return SECSuccess;
287
288 loser:
289 if (signature.len != 0)
290 SECITEM_FreeItem(&signature, PR_FALSE);
291 if (privkey)
292 SECKEY_DestroyPrivateKey(privkey);
293 if (tmppoolp)
294 PORT_FreeArena(tmppoolp, PR_FALSE);
295 return SECFailure;
296 }
297
298 SECStatus
NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo * signerinfo,CERTCertDBHandle * certdb,SECCertUsage certusage)299 NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
300 SECCertUsage certusage)
301 {
302 CERTCertificate *cert;
303 PRTime stime;
304
305 if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
306 signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
307 return SECFailure;
308 }
309
310 /*
311 * Get and convert the signing time; if available, it will be used
312 * both on the cert verification and for importing the sender
313 * email profile.
314 */
315 if (NSS_CMSSignerInfo_GetSigningTime(signerinfo, &stime) != SECSuccess)
316 stime = PR_Now(); /* not found or conversion failed, so check against now */
317
318 /*
319 * XXX This uses the signing time, if available. Additionally, we
320 * might want to, if there is no signing time, get the message time
321 * from the mail header itself, and use that. That would require
322 * a change to our interface though, and for S/MIME callers to pass
323 * in a time (and for non-S/MIME callers to pass in nothing, or
324 * maybe make them pass in the current time, always?).
325 */
326 if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime,
327 signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
328 signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
329 return SECFailure;
330 }
331 return SECSuccess;
332 }
333
334 /*
335 * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
336 *
337 * Just verifies the signature. The assumption is that verification of
338 * the certificate is done already.
339 */
340 SECStatus
NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo * signerinfo,SECItem * digest,SECItem * contentType)341 NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo,
342 SECItem *digest, /* may be NULL */
343 SECItem *contentType) /* may be NULL */
344 {
345 SECKEYPublicKey *publickey = NULL;
346 NSSCMSAttribute *attr;
347 SECItem encoded_attrs;
348 CERTCertificate *cert;
349 NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
350 PLArenaPool *poolp;
351 SECOidTag digestalgtag;
352 SECOidTag pubkAlgTag;
353 SECOidTag digestalgtagCmp;
354 SECOidTag sigAlgTag;
355
356 if (signerinfo == NULL)
357 return SECFailure;
358
359 /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL
360 ** and cert has not been verified
361 */
362 cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
363 if (cert == NULL) {
364 vs = NSSCMSVS_SigningCertNotFound;
365 goto loser;
366 }
367
368 if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
369 vs = NSSCMSVS_ProcessingError;
370 goto loser;
371 }
372
373 digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
374 pubkAlgTag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
375 sigAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg));
376 if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN) ||
377 (sigAlgTag == SEC_OID_UNKNOWN)) {
378 vs = NSSCMSVS_SignatureAlgorithmUnknown;
379 goto loser;
380 }
381
382 if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
383 if (contentType) {
384 /*
385 * Check content type
386 *
387 * RFC2630 sez that if there are any authenticated attributes,
388 * then there must be one for content type which matches the
389 * content type of the content being signed, and there must
390 * be one for message digest which matches our message digest.
391 * So check these things first.
392 */
393 attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
394 SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
395 if (attr == NULL) {
396 vs = NSSCMSVS_MalformedSignature;
397 goto loser;
398 }
399
400 if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
401 vs = NSSCMSVS_MalformedSignature;
402 goto loser;
403 }
404 }
405
406 /*
407 * Check digest
408 */
409 attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
410 SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
411 if (attr == NULL) {
412 vs = NSSCMSVS_MalformedSignature;
413 goto loser;
414 }
415 if (!digest ||
416 NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
417 vs = NSSCMSVS_DigestMismatch;
418 goto loser;
419 }
420
421 if ((poolp = PORT_NewArena(1024)) == NULL) {
422 vs = NSSCMSVS_ProcessingError;
423 goto loser;
424 }
425
426 /*
427 * Check signature
428 *
429 * The signature is based on a digest of the DER-encoded authenticated
430 * attributes. So, first we encode and then we digest/verify.
431 * we trust the decoder to have the attributes in the right (sorted)
432 * order
433 */
434 encoded_attrs.data = NULL;
435 encoded_attrs.len = 0;
436
437 if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr),
438 &encoded_attrs) == NULL ||
439 encoded_attrs.data == NULL || encoded_attrs.len == 0) {
440 PORT_FreeArena(poolp, PR_FALSE);
441 vs = NSSCMSVS_ProcessingError;
442 goto loser;
443 }
444
445 if (sigAlgTag == pubkAlgTag) {
446 /* This is to handle cases in which signatureAlgorithm field
447 * specifies the public key algorithm rather than a signature
448 * algorithm. */
449 vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len,
450 publickey, &(signerinfo->encDigest), pubkAlgTag,
451 digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess)
452 ? NSSCMSVS_BadSignature
453 : NSSCMSVS_GoodSignature;
454 } else {
455 if (VFY_VerifyDataWithAlgorithmID(encoded_attrs.data,
456 encoded_attrs.len, publickey, &(signerinfo->encDigest),
457 &(signerinfo->digestEncAlg), &digestalgtagCmp,
458 signerinfo->cmsg->pwfn_arg) != SECSuccess) {
459 vs = NSSCMSVS_BadSignature;
460 } else if (digestalgtagCmp != digestalgtag) {
461 PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
462 vs = NSSCMSVS_BadSignature;
463 } else {
464 vs = NSSCMSVS_GoodSignature;
465 }
466 }
467
468 PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
469
470 } else {
471 SECItem *sig;
472
473 /* No authenticated attributes.
474 ** The signature is based on the plain message digest.
475 */
476 sig = &(signerinfo->encDigest);
477 if (sig->len == 0)
478 goto loser;
479
480 if (sigAlgTag == pubkAlgTag) {
481 /* This is to handle cases in which signatureAlgorithm field
482 * specifies the public key algorithm rather than a signature
483 * algorithm. */
484 vs = (!digest ||
485 VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag,
486 digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess)
487 ? NSSCMSVS_BadSignature
488 : NSSCMSVS_GoodSignature;
489 } else {
490 vs = (!digest ||
491 VFY_VerifyDigestWithAlgorithmID(digest, publickey, sig,
492 &(signerinfo->digestEncAlg), digestalgtag,
493 signerinfo->cmsg->pwfn_arg) != SECSuccess)
494 ? NSSCMSVS_BadSignature
495 : NSSCMSVS_GoodSignature;
496 }
497 }
498
499 if (vs == NSSCMSVS_BadSignature) {
500 int error = PORT_GetError();
501 /*
502 * XXX Change the generic error into our specific one, because
503 * in that case we get a better explanation out of the Security
504 * Advisor. This is really a bug in the PSM error strings (the
505 * "generic" error has a lousy/wrong message associated with it
506 * which assumes the signature verification was done for the
507 * purposes of checking the issuer signature on a certificate)
508 * but this is at least an easy workaround and/or in the
509 * Security Advisor, which specifically checks for the error
510 * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
511 * in that case but does not similarly check for
512 * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
513 * probably say the wrong thing in the case that it *was* the
514 * certificate signature check that failed during the cert
515 * verification done above. Our error handling is really a mess.
516 */
517 if (error == SEC_ERROR_BAD_SIGNATURE)
518 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
519 /*
520 * map algorithm failures to NSSCMSVS values
521 */
522 if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) ||
523 (error == SEC_ERROR_INVALID_ALGORITHM)) {
524 /* keep the same error code as 3.11 and before */
525 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
526 vs = NSSCMSVS_SignatureAlgorithmUnsupported;
527 }
528 }
529
530 if (publickey != NULL)
531 SECKEY_DestroyPublicKey(publickey);
532
533 signerinfo->verificationStatus = vs;
534
535 return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
536
537 loser:
538 if (publickey != NULL)
539 SECKEY_DestroyPublicKey(publickey);
540
541 signerinfo->verificationStatus = vs;
542
543 PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE);
544 return SECFailure;
545 }
546
547 NSSCMSVerificationStatus
NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo * signerinfo)548 NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
549 {
550 return signerinfo->verificationStatus;
551 }
552
553 SECOidData *
NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo * signerinfo)554 NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
555 {
556 SECOidData *algdata;
557 SECOidTag algtag;
558
559 algdata = SECOID_FindOID(&(signerinfo->digestAlg.algorithm));
560 if (algdata == NULL) {
561 return algdata;
562 }
563 /* Windows may have given us a signer algorithm oid instead of a digest
564 * algorithm oid. This call will map to a signer oid to a digest one,
565 * otherwise it leaves the oid alone and let the chips fall as they may
566 * if it's not a digest oid.
567 */
568 algtag = NSS_CMSUtil_MapSignAlgs(algdata->offset);
569 if (algtag != algdata->offset) {
570 /* if the tags don't match, then we must have received a signer
571 * algorithID. Now we need to get the oid data for the digest
572 * oid, which the rest of the code is expecting */
573 algdata = SECOID_FindOIDByTag(algtag);
574 }
575
576 return algdata;
577 }
578
579 SECOidTag
NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo * signerinfo)580 NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
581 {
582 SECOidData *algdata;
583
584 if (!signerinfo) {
585 PORT_SetError(SEC_ERROR_INVALID_ARGS);
586 return SEC_OID_UNKNOWN;
587 }
588
589 algdata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
590 if (algdata != NULL)
591 return algdata->offset;
592 else
593 return SEC_OID_UNKNOWN;
594 }
595
596 CERTCertificateList *
NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo * signerinfo)597 NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
598 {
599 return signerinfo->certList;
600 }
601
602 int
NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo * signerinfo)603 NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
604 {
605 unsigned long version;
606
607 /* always take apart the SECItem */
608 if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
609 return 0;
610 else
611 return (int)version;
612 }
613
614 /*
615 * NSS_CMSSignerInfo_GetSigningTime - return the signing time,
616 * in UTCTime or GeneralizedTime format,
617 * of a CMS signerInfo.
618 *
619 * sinfo - signerInfo data for this signer
620 *
621 * Returns a pointer to XXXX (what?)
622 * A return value of NULL is an error.
623 */
624 SECStatus
NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo * sinfo,PRTime * stime)625 NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime)
626 {
627 NSSCMSAttribute *attr;
628 SECItem *value;
629
630 if (sinfo == NULL)
631 return SECFailure;
632
633 if (sinfo->signingTime != 0) {
634 *stime = sinfo->signingTime; /* cached copy */
635 return SECSuccess;
636 }
637
638 attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr,
639 SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
640 /* XXXX multi-valued attributes NIH */
641 if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
642 return SECFailure;
643 if (DER_DecodeTimeChoice(stime, value) != SECSuccess)
644 return SECFailure;
645 sinfo->signingTime = *stime; /* make cached copy */
646 return SECSuccess;
647 }
648
649 /*
650 * Return the signing cert of a CMS signerInfo.
651 *
652 * the certs in the enclosing SignedData must have been imported already
653 */
654 CERTCertificate *
NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo * signerinfo,CERTCertDBHandle * certdb)655 NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb)
656 {
657 CERTCertificate *cert;
658 NSSCMSSignerIdentifier *sid;
659
660 if (signerinfo->cert != NULL)
661 return signerinfo->cert;
662
663 /* no certdb, and cert hasn't been set yet? */
664 if (certdb == NULL)
665 return NULL;
666
667 /*
668 * This cert will also need to be freed, but since we save it
669 * in signerinfo for later, we do not want to destroy it when
670 * we leave this function -- we let the clean-up of the entire
671 * cinfo structure later do the destroy of this cert.
672 */
673 sid = &signerinfo->signerIdentifier;
674 switch (sid->identifierType) {
675 case NSSCMSSignerID_IssuerSN:
676 cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN);
677 break;
678 case NSSCMSSignerID_SubjectKeyID:
679 cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID);
680 break;
681 default:
682 cert = NULL;
683 break;
684 }
685
686 /* cert can be NULL at that point */
687 signerinfo->cert = cert; /* earmark it */
688
689 return cert;
690 }
691
692 /*
693 * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
694 *
695 * sinfo - signerInfo data for this signer
696 *
697 * Returns a pointer to allocated memory, which must be freed with PORT_Free.
698 * A return value of NULL is an error.
699 */
700 char *
NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo * sinfo)701 NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
702 {
703 CERTCertificate *signercert;
704
705 /* will fail if cert is not verified */
706 if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
707 return NULL;
708
709 return (CERT_GetCommonName(&signercert->subject));
710 }
711
712 /*
713 * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
714 *
715 * sinfo - signerInfo data for this signer
716 *
717 * Returns a pointer to allocated memory, which must be freed.
718 * A return value of NULL is an error.
719 */
720 char *
NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo * sinfo)721 NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
722 {
723 CERTCertificate *signercert;
724
725 if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
726 return NULL;
727
728 if (!signercert->emailAddr || !signercert->emailAddr[0])
729 return NULL;
730
731 return (PORT_Strdup(signercert->emailAddr));
732 }
733
734 /*
735 * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
736 * authenticated (i.e. signed) attributes of "signerinfo".
737 */
738 SECStatus
NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo * signerinfo,NSSCMSAttribute * attr)739 NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
740 {
741 return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
742 }
743
744 /*
745 * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
746 * unauthenticated attributes of "signerinfo".
747 */
748 SECStatus
NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo * signerinfo,NSSCMSAttribute * attr)749 NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
750 {
751 return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
752 }
753
754 /*
755 * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
756 * authenticated (i.e. signed) attributes of "signerinfo".
757 *
758 * This is expected to be included in outgoing signed
759 * messages for email (S/MIME) but is likely useful in other situations.
760 *
761 * This should only be added once; a second call will do nothing.
762 *
763 * XXX This will probably just shove the current time into "signerinfo"
764 * but it will not actually get signed until the entire item is
765 * processed for encoding. Is this (expected to be small) delay okay?
766 */
767 SECStatus
NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo * signerinfo,PRTime t)768 NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t)
769 {
770 NSSCMSAttribute *attr;
771 SECItem stime;
772 void *mark;
773 PLArenaPool *poolp;
774
775 poolp = signerinfo->cmsg->poolp;
776
777 mark = PORT_ArenaMark(poolp);
778
779 /* create new signing time attribute */
780 if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess)
781 goto loser;
782
783 if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
784 SECITEM_FreeItem(&stime, PR_FALSE);
785 goto loser;
786 }
787
788 SECITEM_FreeItem(&stime, PR_FALSE);
789
790 if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
791 goto loser;
792
793 PORT_ArenaUnmark(poolp, mark);
794
795 return SECSuccess;
796
797 loser:
798 PORT_ArenaRelease(poolp, mark);
799 return SECFailure;
800 }
801
802 /*
803 * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the
804 * authenticated (i.e. signed) attributes of "signerinfo".
805 *
806 * This is expected to be included in outgoing signed
807 * messages for email (S/MIME).
808 */
809 SECStatus
NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo * signerinfo)810 NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo)
811 {
812 NSSCMSAttribute *attr;
813 SECItem *smimecaps = NULL;
814 void *mark;
815 PLArenaPool *poolp;
816
817 poolp = signerinfo->cmsg->poolp;
818
819 mark = PORT_ArenaMark(poolp);
820
821 smimecaps = SECITEM_AllocItem(poolp, NULL, 0);
822 if (smimecaps == NULL)
823 goto loser;
824
825 /* create new signing time attribute */
826 if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps) != SECSuccess)
827 goto loser;
828
829 if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL)
830 goto loser;
831
832 if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
833 goto loser;
834
835 PORT_ArenaUnmark(poolp, mark);
836 return SECSuccess;
837
838 loser:
839 PORT_ArenaRelease(poolp, mark);
840 return SECFailure;
841 }
842
843 /*
844 * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
845 * authenticated (i.e. signed) attributes of "signerinfo".
846 *
847 * This is expected to be included in outgoing signed messages for email (S/MIME).
848 */
849 SECStatus
NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo * signerinfo,CERTCertificate * cert,CERTCertDBHandle * certdb)850 NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
851 {
852 NSSCMSAttribute *attr;
853 SECItem *smimeekp = NULL;
854 void *mark;
855 PLArenaPool *poolp;
856
857 /* verify this cert for encryption */
858 if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
859 return SECFailure;
860 }
861
862 poolp = signerinfo->cmsg->poolp;
863 mark = PORT_ArenaMark(poolp);
864
865 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
866 if (smimeekp == NULL)
867 goto loser;
868
869 /* create new signing time attribute */
870 if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
871 goto loser;
872
873 if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
874 goto loser;
875
876 if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
877 goto loser;
878
879 PORT_ArenaUnmark(poolp, mark);
880 return SECSuccess;
881
882 loser:
883 PORT_ArenaRelease(poolp, mark);
884 return SECFailure;
885 }
886
887 /*
888 * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the
889 * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft.
890 *
891 * This is expected to be included in outgoing signed messages for email (S/MIME),
892 * if compatibility with Microsoft mail clients is wanted.
893 */
894 SECStatus
NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo * signerinfo,CERTCertificate * cert,CERTCertDBHandle * certdb)895 NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb)
896 {
897 NSSCMSAttribute *attr;
898 SECItem *smimeekp = NULL;
899 void *mark;
900 PLArenaPool *poolp;
901
902 /* verify this cert for encryption */
903 if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
904 return SECFailure;
905 }
906
907 poolp = signerinfo->cmsg->poolp;
908 mark = PORT_ArenaMark(poolp);
909
910 smimeekp = SECITEM_AllocItem(poolp, NULL, 0);
911 if (smimeekp == NULL)
912 goto loser;
913
914 /* create new signing time attribute */
915 if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess)
916 goto loser;
917
918 if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL)
919 goto loser;
920
921 if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
922 goto loser;
923
924 PORT_ArenaUnmark(poolp, mark);
925 return SECSuccess;
926
927 loser:
928 PORT_ArenaRelease(poolp, mark);
929 return SECFailure;
930 }
931
932 /*
933 * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
934 *
935 * 1. digest the DER-encoded signature value of the original signerinfo
936 * 2. create new signerinfo with correct version, sid, digestAlg
937 * 3. add message-digest authAttr, but NO content-type
938 * 4. sign the authAttrs
939 * 5. DER-encode the new signerInfo
940 * 6. add the whole thing to original signerInfo's unAuthAttrs
941 * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute
942 *
943 * XXXX give back the new signerinfo?
944 */
945 SECStatus
NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo * signerinfo,SECOidTag digestalg,CERTCertificate signingcert)946 NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
947 SECOidTag digestalg, CERTCertificate signingcert)
948 {
949 /* XXXX TBD XXXX */
950 return SECFailure;
951 }
952
953 /*
954 * XXXX the following needs to be done in the S/MIME layer code
955 * after signature of a signerinfo is verified
956 */
957 SECStatus
NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo * signerinfo)958 NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
959 {
960 CERTCertificate *cert = NULL;
961 SECItem *profile = NULL;
962 NSSCMSAttribute *attr;
963 SECItem *stime = NULL;
964 SECItem *ekp;
965 CERTCertDBHandle *certdb;
966 int save_error;
967 SECStatus rv;
968 PRBool must_free_cert = PR_FALSE;
969
970 certdb = CERT_GetDefaultCertDB();
971
972 /* sanity check - see if verification status is ok (unverified does not count...) */
973 if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature)
974 return SECFailure;
975
976 /* find preferred encryption cert */
977 if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) &&
978 (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
979 SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */
980 ekp = NSS_CMSAttribute_GetValue(attr);
981 if (ekp == NULL)
982 return SECFailure;
983
984 /* we assume that all certs coming with the message have been imported to the */
985 /* temporary database */
986 cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp);
987 if (cert == NULL)
988 return SECFailure;
989 must_free_cert = PR_TRUE;
990 }
991
992 if (cert == NULL) {
993 /* no preferred cert found?
994 * find the cert the signerinfo is signed with instead */
995 cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb);
996 if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0])
997 return SECFailure;
998 }
999
1000 /* verify this cert for encryption (has been verified for signing so far) */
1001 /* don't verify this cert for encryption. It may just be a signing cert.
1002 * that's OK, we can still save the S/MIME profile. The encryption cert
1003 * should have already been saved */
1004 #ifdef notdef
1005 if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
1006 if (must_free_cert)
1007 CERT_DestroyCertificate(cert);
1008 return SECFailure;
1009 }
1010 #endif
1011
1012 /* XXX store encryption cert permanently? */
1013
1014 /*
1015 * Remember the current error set because we do not care about
1016 * anything set by the functions we are about to call.
1017 */
1018 save_error = PORT_GetError();
1019
1020 if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
1021 attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
1022 SEC_OID_PKCS9_SMIME_CAPABILITIES,
1023 PR_TRUE);
1024 profile = NSS_CMSAttribute_GetValue(attr);
1025 attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
1026 SEC_OID_PKCS9_SIGNING_TIME,
1027 PR_TRUE);
1028 stime = NSS_CMSAttribute_GetValue(attr);
1029 }
1030
1031 rv = CERT_SaveSMimeProfile(cert, profile, stime);
1032 if (must_free_cert)
1033 CERT_DestroyCertificate(cert);
1034
1035 /*
1036 * Restore the saved error in case the calls above set a new
1037 * one that we do not actually care about.
1038 */
1039 PORT_SetError(save_error);
1040
1041 return rv;
1042 }
1043
1044 /*
1045 * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
1046 */
1047 SECStatus
NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo * signerinfo,NSSCMSCertChainMode cm,SECCertUsage usage)1048 NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo,
1049 NSSCMSCertChainMode cm, SECCertUsage usage)
1050 {
1051 if (signerinfo->cert == NULL)
1052 return SECFailure;
1053
1054 /* don't leak if we get called twice */
1055 if (signerinfo->certList != NULL) {
1056 CERT_DestroyCertificateList(signerinfo->certList);
1057 signerinfo->certList = NULL;
1058 }
1059
1060 switch (cm) {
1061 case NSSCMSCM_None:
1062 signerinfo->certList = NULL;
1063 break;
1064 case NSSCMSCM_CertOnly:
1065 signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
1066 break;
1067 case NSSCMSCM_CertChain:
1068 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
1069 usage, PR_FALSE);
1070 break;
1071 case NSSCMSCM_CertChainWithRoot:
1072 signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert,
1073 usage, PR_TRUE);
1074 break;
1075 }
1076
1077 if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
1078 return SECFailure;
1079
1080 return SECSuccess;
1081 }
1082