1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/nss/lib/smime/cmssiginfo.c Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1014 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +/* 1.9 + * CMS signerInfo methods. 1.10 + */ 1.11 + 1.12 +#include "cmslocal.h" 1.13 + 1.14 +#include "cert.h" 1.15 +#include "key.h" 1.16 +#include "secasn1.h" 1.17 +#include "secitem.h" 1.18 +#include "secoid.h" 1.19 +#include "pk11func.h" 1.20 +#include "prtime.h" 1.21 +#include "secerr.h" 1.22 +#include "secder.h" 1.23 +#include "cryptohi.h" 1.24 + 1.25 +#include "smime.h" 1.26 + 1.27 +/* ============================================================================= 1.28 + * SIGNERINFO 1.29 + */ 1.30 +NSSCMSSignerInfo * 1.31 +nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type, 1.32 + CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey, 1.33 + SECKEYPrivateKey *signingKey, SECOidTag digestalgtag); 1.34 + 1.35 +NSSCMSSignerInfo * 1.36 +NSS_CMSSignerInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, SECItem *subjKeyID, 1.37 + SECKEYPublicKey *pubKey, SECKEYPrivateKey *signingKey, SECOidTag digestalgtag) 1.38 +{ 1.39 + return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_SubjectKeyID, NULL, subjKeyID, pubKey, signingKey, digestalgtag); 1.40 +} 1.41 + 1.42 +NSSCMSSignerInfo * 1.43 +NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag) 1.44 +{ 1.45 + return nss_cmssignerinfo_create(cmsg, NSSCMSSignerID_IssuerSN, cert, NULL, NULL, NULL, digestalgtag); 1.46 +} 1.47 + 1.48 +NSSCMSSignerInfo * 1.49 +nss_cmssignerinfo_create(NSSCMSMessage *cmsg, NSSCMSSignerIDSelector type, 1.50 + CERTCertificate *cert, SECItem *subjKeyID, SECKEYPublicKey *pubKey, 1.51 + SECKEYPrivateKey *signingKey, SECOidTag digestalgtag) 1.52 +{ 1.53 + void *mark; 1.54 + NSSCMSSignerInfo *signerinfo; 1.55 + int version; 1.56 + PLArenaPool *poolp; 1.57 + 1.58 + poolp = cmsg->poolp; 1.59 + 1.60 + mark = PORT_ArenaMark(poolp); 1.61 + 1.62 + signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo)); 1.63 + if (signerinfo == NULL) { 1.64 + PORT_ArenaRelease(poolp, mark); 1.65 + return NULL; 1.66 + } 1.67 + 1.68 + 1.69 + signerinfo->cmsg = cmsg; 1.70 + 1.71 + switch(type) { 1.72 + case NSSCMSSignerID_IssuerSN: 1.73 + signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN; 1.74 + if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL) 1.75 + goto loser; 1.76 + if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) 1.77 + goto loser; 1.78 + break; 1.79 + case NSSCMSSignerID_SubjectKeyID: 1.80 + signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_SubjectKeyID; 1.81 + PORT_Assert(subjKeyID); 1.82 + if (!subjKeyID) 1.83 + goto loser; 1.84 + 1.85 + signerinfo->signerIdentifier.id.subjectKeyID = PORT_ArenaNew(poolp, SECItem); 1.86 + SECITEM_CopyItem(poolp, signerinfo->signerIdentifier.id.subjectKeyID, 1.87 + subjKeyID); 1.88 + signerinfo->signingKey = SECKEY_CopyPrivateKey(signingKey); 1.89 + if (!signerinfo->signingKey) 1.90 + goto loser; 1.91 + signerinfo->pubKey = SECKEY_CopyPublicKey(pubKey); 1.92 + if (!signerinfo->pubKey) 1.93 + goto loser; 1.94 + break; 1.95 + default: 1.96 + goto loser; 1.97 + } 1.98 + 1.99 + /* set version right now */ 1.100 + version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN; 1.101 + /* RFC2630 5.3 "version is the syntax version number. If the .... " */ 1.102 + if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID) 1.103 + version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY; 1.104 + (void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version); 1.105 + 1.106 + if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess) 1.107 + goto loser; 1.108 + 1.109 + PORT_ArenaUnmark(poolp, mark); 1.110 + return signerinfo; 1.111 + 1.112 +loser: 1.113 + PORT_ArenaRelease(poolp, mark); 1.114 + return NULL; 1.115 +} 1.116 + 1.117 +/* 1.118 + * NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure 1.119 + */ 1.120 +void 1.121 +NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si) 1.122 +{ 1.123 + if (si->cert != NULL) 1.124 + CERT_DestroyCertificate(si->cert); 1.125 + 1.126 + if (si->certList != NULL) 1.127 + CERT_DestroyCertificateList(si->certList); 1.128 + 1.129 + /* XXX storage ??? */ 1.130 +} 1.131 + 1.132 +/* 1.133 + * NSS_CMSSignerInfo_Sign - sign something 1.134 + * 1.135 + */ 1.136 +SECStatus 1.137 +NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, 1.138 + SECItem *contentType) 1.139 +{ 1.140 + CERTCertificate *cert; 1.141 + SECKEYPrivateKey *privkey = NULL; 1.142 + SECOidTag digestalgtag; 1.143 + SECOidTag pubkAlgTag; 1.144 + SECItem signature = { 0 }; 1.145 + SECStatus rv; 1.146 + PLArenaPool *poolp, *tmppoolp = NULL; 1.147 + SECAlgorithmID *algID, freeAlgID; 1.148 + CERTSubjectPublicKeyInfo *spki; 1.149 + 1.150 + PORT_Assert (digest != NULL); 1.151 + 1.152 + poolp = signerinfo->cmsg->poolp; 1.153 + 1.154 + switch (signerinfo->signerIdentifier.identifierType) { 1.155 + case NSSCMSSignerID_IssuerSN: 1.156 + cert = signerinfo->cert; 1.157 + 1.158 + privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg); 1.159 + if (privkey == NULL) 1.160 + goto loser; 1.161 + algID = &cert->subjectPublicKeyInfo.algorithm; 1.162 + break; 1.163 + case NSSCMSSignerID_SubjectKeyID: 1.164 + privkey = signerinfo->signingKey; 1.165 + signerinfo->signingKey = NULL; 1.166 + spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey); 1.167 + SECKEY_DestroyPublicKey(signerinfo->pubKey); 1.168 + signerinfo->pubKey = NULL; 1.169 + SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm); 1.170 + SECKEY_DestroySubjectPublicKeyInfo(spki); 1.171 + algID = &freeAlgID; 1.172 + break; 1.173 + default: 1.174 + goto loser; 1.175 + } 1.176 + digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); 1.177 + /* 1.178 + * XXX I think there should be a cert-level interface for this, 1.179 + * so that I do not have to know about subjectPublicKeyInfo... 1.180 + */ 1.181 + pubkAlgTag = SECOID_GetAlgorithmTag(algID); 1.182 + if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID) { 1.183 + SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE); 1.184 + } 1.185 + 1.186 + if (signerinfo->authAttr != NULL) { 1.187 + SECOidTag signAlgTag; 1.188 + SECItem encoded_attrs; 1.189 + 1.190 + /* find and fill in the message digest attribute. */ 1.191 + rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 1.192 + SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE); 1.193 + if (rv != SECSuccess) 1.194 + goto loser; 1.195 + 1.196 + if (contentType != NULL) { 1.197 + /* if the caller wants us to, find and fill in the content type attribute. */ 1.198 + rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 1.199 + SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE); 1.200 + if (rv != SECSuccess) 1.201 + goto loser; 1.202 + } 1.203 + 1.204 + if ((tmppoolp = PORT_NewArena (1024)) == NULL) { 1.205 + PORT_SetError(SEC_ERROR_NO_MEMORY); 1.206 + goto loser; 1.207 + } 1.208 + 1.209 + /* 1.210 + * Before encoding, reorder the attributes so that when they 1.211 + * are encoded, they will be conforming DER, which is required 1.212 + * to have a specific order and that is what must be used for 1.213 + * the hash/signature. We do this here, rather than building 1.214 + * it into EncodeAttributes, because we do not want to do 1.215 + * such reordering on incoming messages (which also uses 1.216 + * EncodeAttributes) or our old signatures (and other "broken" 1.217 + * implementations) will not verify. So, we want to guarantee 1.218 + * that we send out good DER encodings of attributes, but not 1.219 + * to expect to receive them. 1.220 + */ 1.221 + if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess) 1.222 + goto loser; 1.223 + 1.224 + encoded_attrs.data = NULL; 1.225 + encoded_attrs.len = 0; 1.226 + if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr), 1.227 + &encoded_attrs) == NULL) 1.228 + goto loser; 1.229 + 1.230 + signAlgTag = SEC_GetSignatureAlgorithmOidTag(privkey->keyType, 1.231 + digestalgtag); 1.232 + if (signAlgTag == SEC_OID_UNKNOWN) { 1.233 + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); 1.234 + goto loser; 1.235 + } 1.236 + 1.237 + rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len, 1.238 + privkey, signAlgTag); 1.239 + PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */ 1.240 + tmppoolp = 0; 1.241 + } else { 1.242 + rv = SGN_Digest(privkey, digestalgtag, &signature, digest); 1.243 + } 1.244 + SECKEY_DestroyPrivateKey(privkey); 1.245 + privkey = NULL; 1.246 + 1.247 + if (rv != SECSuccess) 1.248 + goto loser; 1.249 + 1.250 + if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) 1.251 + != SECSuccess) 1.252 + goto loser; 1.253 + 1.254 + SECITEM_FreeItem(&signature, PR_FALSE); 1.255 + 1.256 + if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, 1.257 + NULL) != SECSuccess) 1.258 + goto loser; 1.259 + 1.260 + return SECSuccess; 1.261 + 1.262 +loser: 1.263 + if (signature.len != 0) 1.264 + SECITEM_FreeItem (&signature, PR_FALSE); 1.265 + if (privkey) 1.266 + SECKEY_DestroyPrivateKey(privkey); 1.267 + if (tmppoolp) 1.268 + PORT_FreeArena(tmppoolp, PR_FALSE); 1.269 + return SECFailure; 1.270 +} 1.271 + 1.272 +SECStatus 1.273 +NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb, 1.274 + SECCertUsage certusage) 1.275 +{ 1.276 + CERTCertificate *cert; 1.277 + PRTime stime; 1.278 + 1.279 + if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) { 1.280 + signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound; 1.281 + return SECFailure; 1.282 + } 1.283 + 1.284 + /* 1.285 + * Get and convert the signing time; if available, it will be used 1.286 + * both on the cert verification and for importing the sender 1.287 + * email profile. 1.288 + */ 1.289 + if (NSS_CMSSignerInfo_GetSigningTime (signerinfo, &stime) != SECSuccess) 1.290 + stime = PR_Now(); /* not found or conversion failed, so check against now */ 1.291 + 1.292 + /* 1.293 + * XXX This uses the signing time, if available. Additionally, we 1.294 + * might want to, if there is no signing time, get the message time 1.295 + * from the mail header itself, and use that. That would require 1.296 + * a change to our interface though, and for S/MIME callers to pass 1.297 + * in a time (and for non-S/MIME callers to pass in nothing, or 1.298 + * maybe make them pass in the current time, always?). 1.299 + */ 1.300 + if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime, 1.301 + signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { 1.302 + signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted; 1.303 + return SECFailure; 1.304 + } 1.305 + return SECSuccess; 1.306 +} 1.307 + 1.308 +/* 1.309 + * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo 1.310 + * 1.311 + * Just verifies the signature. The assumption is that verification of 1.312 + * the certificate is done already. 1.313 + */ 1.314 +SECStatus 1.315 +NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo, 1.316 + SECItem *digest, /* may be NULL */ 1.317 + SECItem *contentType) /* may be NULL */ 1.318 +{ 1.319 + SECKEYPublicKey *publickey = NULL; 1.320 + NSSCMSAttribute *attr; 1.321 + SECItem encoded_attrs; 1.322 + CERTCertificate *cert; 1.323 + NSSCMSVerificationStatus vs = NSSCMSVS_Unverified; 1.324 + PLArenaPool *poolp; 1.325 + SECOidTag digestalgtag; 1.326 + SECOidTag pubkAlgTag; 1.327 + 1.328 + if (signerinfo == NULL) 1.329 + return SECFailure; 1.330 + 1.331 + /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL 1.332 + ** and cert has not been verified 1.333 + */ 1.334 + cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL); 1.335 + if (cert == NULL) { 1.336 + vs = NSSCMSVS_SigningCertNotFound; 1.337 + goto loser; 1.338 + } 1.339 + 1.340 + if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) { 1.341 + vs = NSSCMSVS_ProcessingError; 1.342 + goto loser; 1.343 + } 1.344 + 1.345 + digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); 1.346 + pubkAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)); 1.347 + if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN)) { 1.348 + vs = NSSCMSVS_SignatureAlgorithmUnknown; 1.349 + goto loser; 1.350 + } 1.351 + 1.352 + if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) { 1.353 + if (contentType) { 1.354 + /* 1.355 + * Check content type 1.356 + * 1.357 + * RFC2630 sez that if there are any authenticated attributes, 1.358 + * then there must be one for content type which matches the 1.359 + * content type of the content being signed, and there must 1.360 + * be one for message digest which matches our message digest. 1.361 + * So check these things first. 1.362 + */ 1.363 + attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 1.364 + SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE); 1.365 + if (attr == NULL) { 1.366 + vs = NSSCMSVS_MalformedSignature; 1.367 + goto loser; 1.368 + } 1.369 + 1.370 + if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) { 1.371 + vs = NSSCMSVS_MalformedSignature; 1.372 + goto loser; 1.373 + } 1.374 + } 1.375 + 1.376 + /* 1.377 + * Check digest 1.378 + */ 1.379 + attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 1.380 + SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); 1.381 + if (attr == NULL) { 1.382 + vs = NSSCMSVS_MalformedSignature; 1.383 + goto loser; 1.384 + } 1.385 + if (!digest || 1.386 + NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) { 1.387 + vs = NSSCMSVS_DigestMismatch; 1.388 + goto loser; 1.389 + } 1.390 + 1.391 + if ((poolp = PORT_NewArena (1024)) == NULL) { 1.392 + vs = NSSCMSVS_ProcessingError; 1.393 + goto loser; 1.394 + } 1.395 + 1.396 + /* 1.397 + * Check signature 1.398 + * 1.399 + * The signature is based on a digest of the DER-encoded authenticated 1.400 + * attributes. So, first we encode and then we digest/verify. 1.401 + * we trust the decoder to have the attributes in the right (sorted) 1.402 + * order 1.403 + */ 1.404 + encoded_attrs.data = NULL; 1.405 + encoded_attrs.len = 0; 1.406 + 1.407 + if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr), 1.408 + &encoded_attrs) == NULL || 1.409 + encoded_attrs.data == NULL || encoded_attrs.len == 0) { 1.410 + vs = NSSCMSVS_ProcessingError; 1.411 + goto loser; 1.412 + } 1.413 + 1.414 + vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len, 1.415 + publickey, &(signerinfo->encDigest), pubkAlgTag, 1.416 + digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess) 1.417 + ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature; 1.418 + 1.419 + PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */ 1.420 + 1.421 + } else { 1.422 + SECItem *sig; 1.423 + 1.424 + /* No authenticated attributes. 1.425 + ** The signature is based on the plain message digest. 1.426 + */ 1.427 + sig = &(signerinfo->encDigest); 1.428 + if (sig->len == 0) 1.429 + goto loser; 1.430 + 1.431 + vs = (!digest || 1.432 + VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag, 1.433 + digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess) 1.434 + ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature; 1.435 + } 1.436 + 1.437 + if (vs == NSSCMSVS_BadSignature) { 1.438 + int error = PORT_GetError(); 1.439 + /* 1.440 + * XXX Change the generic error into our specific one, because 1.441 + * in that case we get a better explanation out of the Security 1.442 + * Advisor. This is really a bug in the PSM error strings (the 1.443 + * "generic" error has a lousy/wrong message associated with it 1.444 + * which assumes the signature verification was done for the 1.445 + * purposes of checking the issuer signature on a certificate) 1.446 + * but this is at least an easy workaround and/or in the 1.447 + * Security Advisor, which specifically checks for the error 1.448 + * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation 1.449 + * in that case but does not similarly check for 1.450 + * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would 1.451 + * probably say the wrong thing in the case that it *was* the 1.452 + * certificate signature check that failed during the cert 1.453 + * verification done above. Our error handling is really a mess. 1.454 + */ 1.455 + if (error == SEC_ERROR_BAD_SIGNATURE) 1.456 + PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); 1.457 + /* 1.458 + * map algorithm failures to NSSCMSVS values 1.459 + */ 1.460 + if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) || 1.461 + (error == SEC_ERROR_INVALID_ALGORITHM)) { 1.462 + /* keep the same error code as 3.11 and before */ 1.463 + PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); 1.464 + vs = NSSCMSVS_SignatureAlgorithmUnsupported; 1.465 + } 1.466 + } 1.467 + 1.468 + if (publickey != NULL) 1.469 + SECKEY_DestroyPublicKey (publickey); 1.470 + 1.471 + signerinfo->verificationStatus = vs; 1.472 + 1.473 + return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure; 1.474 + 1.475 +loser: 1.476 + if (publickey != NULL) 1.477 + SECKEY_DestroyPublicKey (publickey); 1.478 + 1.479 + signerinfo->verificationStatus = vs; 1.480 + 1.481 + PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); 1.482 + return SECFailure; 1.483 +} 1.484 + 1.485 +NSSCMSVerificationStatus 1.486 +NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo) 1.487 +{ 1.488 + return signerinfo->verificationStatus; 1.489 +} 1.490 + 1.491 +SECOidData * 1.492 +NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo) 1.493 +{ 1.494 + SECOidData *algdata; 1.495 + SECOidTag algtag; 1.496 + 1.497 + algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); 1.498 + if (algdata == NULL) { 1.499 + return algdata; 1.500 + } 1.501 + /* Windows may have given us a signer algorithm oid instead of a digest 1.502 + * algorithm oid. This call will map to a signer oid to a digest one, 1.503 + * otherwise it leaves the oid alone and let the chips fall as they may 1.504 + * if it's not a digest oid. 1.505 + */ 1.506 + algtag = NSS_CMSUtil_MapSignAlgs(algdata->offset); 1.507 + if (algtag != algdata->offset) { 1.508 + /* if the tags don't match, then we must have received a signer 1.509 + * algorithID. Now we need to get the oid data for the digest 1.510 + * oid, which the rest of the code is expecting */ 1.511 + algdata = SECOID_FindOIDByTag(algtag); 1.512 + } 1.513 + 1.514 + return algdata; 1.515 + 1.516 +} 1.517 + 1.518 +SECOidTag 1.519 +NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo) 1.520 +{ 1.521 + SECOidData *algdata; 1.522 + 1.523 + if (!signerinfo) { 1.524 + PORT_SetError(SEC_ERROR_INVALID_ARGS); 1.525 + return SEC_OID_UNKNOWN; 1.526 + } 1.527 + 1.528 + algdata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo); 1.529 + if (algdata != NULL) 1.530 + return algdata->offset; 1.531 + else 1.532 + return SEC_OID_UNKNOWN; 1.533 +} 1.534 + 1.535 +CERTCertificateList * 1.536 +NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo) 1.537 +{ 1.538 + return signerinfo->certList; 1.539 +} 1.540 + 1.541 +int 1.542 +NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo) 1.543 +{ 1.544 + unsigned long version; 1.545 + 1.546 + /* always take apart the SECItem */ 1.547 + if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess) 1.548 + return 0; 1.549 + else 1.550 + return (int)version; 1.551 +} 1.552 + 1.553 +/* 1.554 + * NSS_CMSSignerInfo_GetSigningTime - return the signing time, 1.555 + * in UTCTime or GeneralizedTime format, 1.556 + * of a CMS signerInfo. 1.557 + * 1.558 + * sinfo - signerInfo data for this signer 1.559 + * 1.560 + * Returns a pointer to XXXX (what?) 1.561 + * A return value of NULL is an error. 1.562 + */ 1.563 +SECStatus 1.564 +NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime) 1.565 +{ 1.566 + NSSCMSAttribute *attr; 1.567 + SECItem *value; 1.568 + 1.569 + if (sinfo == NULL) 1.570 + return SECFailure; 1.571 + 1.572 + if (sinfo->signingTime != 0) { 1.573 + *stime = sinfo->signingTime; /* cached copy */ 1.574 + return SECSuccess; 1.575 + } 1.576 + 1.577 + attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); 1.578 + /* XXXX multi-valued attributes NIH */ 1.579 + if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL) 1.580 + return SECFailure; 1.581 + if (DER_DecodeTimeChoice(stime, value) != SECSuccess) 1.582 + return SECFailure; 1.583 + sinfo->signingTime = *stime; /* make cached copy */ 1.584 + return SECSuccess; 1.585 +} 1.586 + 1.587 +/* 1.588 + * Return the signing cert of a CMS signerInfo. 1.589 + * 1.590 + * the certs in the enclosing SignedData must have been imported already 1.591 + */ 1.592 +CERTCertificate * 1.593 +NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb) 1.594 +{ 1.595 + CERTCertificate *cert; 1.596 + NSSCMSSignerIdentifier *sid; 1.597 + 1.598 + if (signerinfo->cert != NULL) 1.599 + return signerinfo->cert; 1.600 + 1.601 + /* no certdb, and cert hasn't been set yet? */ 1.602 + if (certdb == NULL) 1.603 + return NULL; 1.604 + 1.605 + /* 1.606 + * This cert will also need to be freed, but since we save it 1.607 + * in signerinfo for later, we do not want to destroy it when 1.608 + * we leave this function -- we let the clean-up of the entire 1.609 + * cinfo structure later do the destroy of this cert. 1.610 + */ 1.611 + sid = &signerinfo->signerIdentifier; 1.612 + switch (sid->identifierType) { 1.613 + case NSSCMSSignerID_IssuerSN: 1.614 + cert = CERT_FindCertByIssuerAndSN(certdb, sid->id.issuerAndSN); 1.615 + break; 1.616 + case NSSCMSSignerID_SubjectKeyID: 1.617 + cert = CERT_FindCertBySubjectKeyID(certdb, sid->id.subjectKeyID); 1.618 + break; 1.619 + default: 1.620 + cert = NULL; 1.621 + break; 1.622 + } 1.623 + 1.624 + /* cert can be NULL at that point */ 1.625 + signerinfo->cert = cert; /* earmark it */ 1.626 + 1.627 + return cert; 1.628 +} 1.629 + 1.630 +/* 1.631 + * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer 1.632 + * 1.633 + * sinfo - signerInfo data for this signer 1.634 + * 1.635 + * Returns a pointer to allocated memory, which must be freed with PORT_Free. 1.636 + * A return value of NULL is an error. 1.637 + */ 1.638 +char * 1.639 +NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo) 1.640 +{ 1.641 + CERTCertificate *signercert; 1.642 + 1.643 + /* will fail if cert is not verified */ 1.644 + if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL) 1.645 + return NULL; 1.646 + 1.647 + return (CERT_GetCommonName(&signercert->subject)); 1.648 +} 1.649 + 1.650 +/* 1.651 + * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer 1.652 + * 1.653 + * sinfo - signerInfo data for this signer 1.654 + * 1.655 + * Returns a pointer to allocated memory, which must be freed. 1.656 + * A return value of NULL is an error. 1.657 + */ 1.658 +char * 1.659 +NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo) 1.660 +{ 1.661 + CERTCertificate *signercert; 1.662 + 1.663 + if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL) 1.664 + return NULL; 1.665 + 1.666 + if (!signercert->emailAddr || !signercert->emailAddr[0]) 1.667 + return NULL; 1.668 + 1.669 + return (PORT_Strdup(signercert->emailAddr)); 1.670 +} 1.671 + 1.672 +/* 1.673 + * NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the 1.674 + * authenticated (i.e. signed) attributes of "signerinfo". 1.675 + */ 1.676 +SECStatus 1.677 +NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr) 1.678 +{ 1.679 + return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr); 1.680 +} 1.681 + 1.682 +/* 1.683 + * NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the 1.684 + * unauthenticated attributes of "signerinfo". 1.685 + */ 1.686 +SECStatus 1.687 +NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr) 1.688 +{ 1.689 + return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr); 1.690 +} 1.691 + 1.692 +/* 1.693 + * NSS_CMSSignerInfo_AddSigningTime - add the signing time to the 1.694 + * authenticated (i.e. signed) attributes of "signerinfo". 1.695 + * 1.696 + * This is expected to be included in outgoing signed 1.697 + * messages for email (S/MIME) but is likely useful in other situations. 1.698 + * 1.699 + * This should only be added once; a second call will do nothing. 1.700 + * 1.701 + * XXX This will probably just shove the current time into "signerinfo" 1.702 + * but it will not actually get signed until the entire item is 1.703 + * processed for encoding. Is this (expected to be small) delay okay? 1.704 + */ 1.705 +SECStatus 1.706 +NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t) 1.707 +{ 1.708 + NSSCMSAttribute *attr; 1.709 + SECItem stime; 1.710 + void *mark; 1.711 + PLArenaPool *poolp; 1.712 + 1.713 + poolp = signerinfo->cmsg->poolp; 1.714 + 1.715 + mark = PORT_ArenaMark(poolp); 1.716 + 1.717 + /* create new signing time attribute */ 1.718 + if (DER_EncodeTimeChoice(NULL, &stime, t) != SECSuccess) 1.719 + goto loser; 1.720 + 1.721 + if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) { 1.722 + SECITEM_FreeItem (&stime, PR_FALSE); 1.723 + goto loser; 1.724 + } 1.725 + 1.726 + SECITEM_FreeItem (&stime, PR_FALSE); 1.727 + 1.728 + if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) 1.729 + goto loser; 1.730 + 1.731 + PORT_ArenaUnmark (poolp, mark); 1.732 + 1.733 + return SECSuccess; 1.734 + 1.735 +loser: 1.736 + PORT_ArenaRelease (poolp, mark); 1.737 + return SECFailure; 1.738 +} 1.739 + 1.740 +/* 1.741 + * NSS_CMSSignerInfo_AddSMIMECaps - add a SMIMECapabilities attribute to the 1.742 + * authenticated (i.e. signed) attributes of "signerinfo". 1.743 + * 1.744 + * This is expected to be included in outgoing signed 1.745 + * messages for email (S/MIME). 1.746 + */ 1.747 +SECStatus 1.748 +NSS_CMSSignerInfo_AddSMIMECaps(NSSCMSSignerInfo *signerinfo) 1.749 +{ 1.750 + NSSCMSAttribute *attr; 1.751 + SECItem *smimecaps = NULL; 1.752 + void *mark; 1.753 + PLArenaPool *poolp; 1.754 + 1.755 + poolp = signerinfo->cmsg->poolp; 1.756 + 1.757 + mark = PORT_ArenaMark(poolp); 1.758 + 1.759 + smimecaps = SECITEM_AllocItem(poolp, NULL, 0); 1.760 + if (smimecaps == NULL) 1.761 + goto loser; 1.762 + 1.763 + /* create new signing time attribute */ 1.764 + if (NSS_SMIMEUtil_CreateSMIMECapabilities(poolp, smimecaps) != SECSuccess) 1.765 + goto loser; 1.766 + 1.767 + if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SMIME_CAPABILITIES, smimecaps, PR_TRUE)) == NULL) 1.768 + goto loser; 1.769 + 1.770 + if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) 1.771 + goto loser; 1.772 + 1.773 + PORT_ArenaUnmark (poolp, mark); 1.774 + return SECSuccess; 1.775 + 1.776 +loser: 1.777 + PORT_ArenaRelease (poolp, mark); 1.778 + return SECFailure; 1.779 +} 1.780 + 1.781 +/* 1.782 + * NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the 1.783 + * authenticated (i.e. signed) attributes of "signerinfo". 1.784 + * 1.785 + * This is expected to be included in outgoing signed messages for email (S/MIME). 1.786 + */ 1.787 +SECStatus 1.788 +NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb) 1.789 +{ 1.790 + NSSCMSAttribute *attr; 1.791 + SECItem *smimeekp = NULL; 1.792 + void *mark; 1.793 + PLArenaPool *poolp; 1.794 + 1.795 + /* verify this cert for encryption */ 1.796 + if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { 1.797 + return SECFailure; 1.798 + } 1.799 + 1.800 + poolp = signerinfo->cmsg->poolp; 1.801 + mark = PORT_ArenaMark(poolp); 1.802 + 1.803 + smimeekp = SECITEM_AllocItem(poolp, NULL, 0); 1.804 + if (smimeekp == NULL) 1.805 + goto loser; 1.806 + 1.807 + /* create new signing time attribute */ 1.808 + if (NSS_SMIMEUtil_CreateSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess) 1.809 + goto loser; 1.810 + 1.811 + if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) 1.812 + goto loser; 1.813 + 1.814 + if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) 1.815 + goto loser; 1.816 + 1.817 + PORT_ArenaUnmark (poolp, mark); 1.818 + return SECSuccess; 1.819 + 1.820 +loser: 1.821 + PORT_ArenaRelease (poolp, mark); 1.822 + return SECFailure; 1.823 +} 1.824 + 1.825 +/* 1.826 + * NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs - add a SMIMEEncryptionKeyPreferences attribute to the 1.827 + * authenticated (i.e. signed) attributes of "signerinfo", using the OID preferred by Microsoft. 1.828 + * 1.829 + * This is expected to be included in outgoing signed messages for email (S/MIME), 1.830 + * if compatibility with Microsoft mail clients is wanted. 1.831 + */ 1.832 +SECStatus 1.833 +NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert, CERTCertDBHandle *certdb) 1.834 +{ 1.835 + NSSCMSAttribute *attr; 1.836 + SECItem *smimeekp = NULL; 1.837 + void *mark; 1.838 + PLArenaPool *poolp; 1.839 + 1.840 + /* verify this cert for encryption */ 1.841 + if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { 1.842 + return SECFailure; 1.843 + } 1.844 + 1.845 + poolp = signerinfo->cmsg->poolp; 1.846 + mark = PORT_ArenaMark(poolp); 1.847 + 1.848 + smimeekp = SECITEM_AllocItem(poolp, NULL, 0); 1.849 + if (smimeekp == NULL) 1.850 + goto loser; 1.851 + 1.852 + /* create new signing time attribute */ 1.853 + if (NSS_SMIMEUtil_CreateMSSMIMEEncKeyPrefs(poolp, smimeekp, cert) != SECSuccess) 1.854 + goto loser; 1.855 + 1.856 + if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_MS_SMIME_ENCRYPTION_KEY_PREFERENCE, smimeekp, PR_TRUE)) == NULL) 1.857 + goto loser; 1.858 + 1.859 + if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess) 1.860 + goto loser; 1.861 + 1.862 + PORT_ArenaUnmark (poolp, mark); 1.863 + return SECSuccess; 1.864 + 1.865 +loser: 1.866 + PORT_ArenaRelease (poolp, mark); 1.867 + return SECFailure; 1.868 +} 1.869 + 1.870 +/* 1.871 + * NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo 1.872 + * 1.873 + * 1. digest the DER-encoded signature value of the original signerinfo 1.874 + * 2. create new signerinfo with correct version, sid, digestAlg 1.875 + * 3. add message-digest authAttr, but NO content-type 1.876 + * 4. sign the authAttrs 1.877 + * 5. DER-encode the new signerInfo 1.878 + * 6. add the whole thing to original signerInfo's unAuthAttrs 1.879 + * as a SEC_OID_PKCS9_COUNTER_SIGNATURE attribute 1.880 + * 1.881 + * XXXX give back the new signerinfo? 1.882 + */ 1.883 +SECStatus 1.884 +NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo, 1.885 + SECOidTag digestalg, CERTCertificate signingcert) 1.886 +{ 1.887 + /* XXXX TBD XXXX */ 1.888 + return SECFailure; 1.889 +} 1.890 + 1.891 +/* 1.892 + * XXXX the following needs to be done in the S/MIME layer code 1.893 + * after signature of a signerinfo is verified 1.894 + */ 1.895 +SECStatus 1.896 +NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo) 1.897 +{ 1.898 + CERTCertificate *cert = NULL; 1.899 + SECItem *profile = NULL; 1.900 + NSSCMSAttribute *attr; 1.901 + SECItem *stime = NULL; 1.902 + SECItem *ekp; 1.903 + CERTCertDBHandle *certdb; 1.904 + int save_error; 1.905 + SECStatus rv; 1.906 + PRBool must_free_cert = PR_FALSE; 1.907 + 1.908 + certdb = CERT_GetDefaultCertDB(); 1.909 + 1.910 + /* sanity check - see if verification status is ok (unverified does not count...) */ 1.911 + if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature) 1.912 + return SECFailure; 1.913 + 1.914 + /* find preferred encryption cert */ 1.915 + if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) && 1.916 + (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 1.917 + SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) 1.918 + { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */ 1.919 + ekp = NSS_CMSAttribute_GetValue(attr); 1.920 + if (ekp == NULL) 1.921 + return SECFailure; 1.922 + 1.923 + /* we assume that all certs coming with the message have been imported to the */ 1.924 + /* temporary database */ 1.925 + cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp); 1.926 + if (cert == NULL) 1.927 + return SECFailure; 1.928 + must_free_cert = PR_TRUE; 1.929 + } 1.930 + 1.931 + if (cert == NULL) { 1.932 + /* no preferred cert found? 1.933 + * find the cert the signerinfo is signed with instead */ 1.934 + cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb); 1.935 + if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0]) 1.936 + return SECFailure; 1.937 + } 1.938 + 1.939 + /* verify this cert for encryption (has been verified for signing so far) */ 1.940 + /* don't verify this cert for encryption. It may just be a signing cert. 1.941 + * that's OK, we can still save the S/MIME profile. The encryption cert 1.942 + * should have already been saved */ 1.943 +#ifdef notdef 1.944 + if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { 1.945 + if (must_free_cert) 1.946 + CERT_DestroyCertificate(cert); 1.947 + return SECFailure; 1.948 + } 1.949 +#endif 1.950 + 1.951 + /* XXX store encryption cert permanently? */ 1.952 + 1.953 + /* 1.954 + * Remember the current error set because we do not care about 1.955 + * anything set by the functions we are about to call. 1.956 + */ 1.957 + save_error = PORT_GetError(); 1.958 + 1.959 + if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) { 1.960 + attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 1.961 + SEC_OID_PKCS9_SMIME_CAPABILITIES, 1.962 + PR_TRUE); 1.963 + profile = NSS_CMSAttribute_GetValue(attr); 1.964 + attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, 1.965 + SEC_OID_PKCS9_SIGNING_TIME, 1.966 + PR_TRUE); 1.967 + stime = NSS_CMSAttribute_GetValue(attr); 1.968 + } 1.969 + 1.970 + rv = CERT_SaveSMimeProfile (cert, profile, stime); 1.971 + if (must_free_cert) 1.972 + CERT_DestroyCertificate(cert); 1.973 + 1.974 + /* 1.975 + * Restore the saved error in case the calls above set a new 1.976 + * one that we do not actually care about. 1.977 + */ 1.978 + PORT_SetError (save_error); 1.979 + 1.980 + return rv; 1.981 +} 1.982 + 1.983 +/* 1.984 + * NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer 1.985 + */ 1.986 +SECStatus 1.987 +NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo, NSSCMSCertChainMode cm, SECCertUsage usage) 1.988 +{ 1.989 + if (signerinfo->cert == NULL) 1.990 + return SECFailure; 1.991 + 1.992 + /* don't leak if we get called twice */ 1.993 + if (signerinfo->certList != NULL) { 1.994 + CERT_DestroyCertificateList(signerinfo->certList); 1.995 + signerinfo->certList = NULL; 1.996 + } 1.997 + 1.998 + switch (cm) { 1.999 + case NSSCMSCM_None: 1.1000 + signerinfo->certList = NULL; 1.1001 + break; 1.1002 + case NSSCMSCM_CertOnly: 1.1003 + signerinfo->certList = CERT_CertListFromCert(signerinfo->cert); 1.1004 + break; 1.1005 + case NSSCMSCM_CertChain: 1.1006 + signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE); 1.1007 + break; 1.1008 + case NSSCMSCM_CertChainWithRoot: 1.1009 + signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE); 1.1010 + break; 1.1011 + } 1.1012 + 1.1013 + if (cm != NSSCMSCM_None && signerinfo->certList == NULL) 1.1014 + return SECFailure; 1.1015 + 1.1016 + return SECSuccess; 1.1017 +}