|
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 } |