security/nss/lib/smime/cmssiginfo.c

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

mercurial