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