/* * NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes */ SECStatus NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECOidTag type, SECItem *value, PRBool encoded) { NSSCMSAttribute *attr; void *mark; mark = PORT_ArenaMark(poolp); /* see if we have one already */ attr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE); if (attr == NULL) { /* not found? create one! */ attr = NSS_CMSAttribute_Create(poolp, type, value, encoded); if (attr == NULL) goto loser; /* and add it to the list */ if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess) goto loser; } else { /* found, shove it in */ /* XXX we need a decent memory model @#$#$!#!!! */ attr->values[0] = value; attr->encoded = encoded; } PORT_ArenaUnmark (poolp, mark); return SECSuccess; loser: PORT_ArenaRelease (poolp, mark); return SECFailure; }
/* * NSS_CMSAttributeArray_AddAttr - add an attribute to an * array of attributes. */ SECStatus NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr) { NSSCMSAttribute *oattr; void *mark; SECOidTag type; mark = PORT_ArenaMark(poolp); /* find oidtag of attr */ type = NSS_CMSAttribute_GetType(attr); /* see if we have one already */ oattr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE); PORT_Assert (oattr == NULL); if (oattr != NULL) goto loser; /* XXX or would it be better to replace it? */ /* no, shove it in */ if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess) goto loser; PORT_ArenaUnmark(poolp, mark); return SECSuccess; loser: PORT_ArenaRelease(poolp, mark); return SECFailure; }
/* * NSS_CMSSignerInfo_GetSigningTime - return the signing time, * in UTCTime or GeneralizedTime format, * of a CMS signerInfo. * * sinfo - signerInfo data for this signer * * Returns a pointer to XXXX (what?) * A return value of NULL is an error. */ SECStatus NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime) { NSSCMSAttribute *attr; SECItem *value; if (sinfo == NULL) return SECFailure; if (sinfo->signingTime != 0) { *stime = sinfo->signingTime; /* cached copy */ return SECSuccess; } attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); /* XXXX multi-valued attributes NIH */ if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL) return SECFailure; if (DER_DecodeTimeChoice(stime, value) != SECSuccess) return SECFailure; sinfo->signingTime = *stime; /* make cached copy */ return SECSuccess; }
/* * XXXX the following needs to be done in the S/MIME layer code * after signature of a signerinfo is verified */ SECStatus NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo) { CERTCertificate *cert = NULL; SECItem *profile = NULL; NSSCMSAttribute *attr; SECItem *stime = NULL; SECItem *ekp; CERTCertDBHandle *certdb; int save_error; SECStatus rv; PRBool must_free_cert = PR_FALSE; certdb = CERT_GetDefaultCertDB(); /* sanity check - see if verification status is ok (unverified does not count...) */ if (signerinfo->verificationStatus != NSSCMSVS_GoodSignature) return SECFailure; /* find preferred encryption cert */ if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr) && (attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE, PR_TRUE)) != NULL) { /* we have a SMIME_ENCRYPTION_KEY_PREFERENCE attribute! */ ekp = NSS_CMSAttribute_GetValue(attr); if (ekp == NULL) return SECFailure; /* we assume that all certs coming with the message have been imported to the */ /* temporary database */ cert = NSS_SMIMEUtil_GetCertFromEncryptionKeyPreference(certdb, ekp); if (cert == NULL) return SECFailure; must_free_cert = PR_TRUE; } if (cert == NULL) { /* no preferred cert found? * find the cert the signerinfo is signed with instead */ cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb); if (cert == NULL || cert->emailAddr == NULL || !cert->emailAddr[0]) return SECFailure; } /* verify this cert for encryption (has been verified for signing so far) */ /* don't verify this cert for encryption. It may just be a signing cert. * that's OK, we can still save the S/MIME profile. The encryption cert * should have already been saved */ #ifdef notdef if (CERT_VerifyCert(certdb, cert, PR_TRUE, certUsageEmailRecipient, PR_Now(), signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) { if (must_free_cert) CERT_DestroyCertificate(cert); return SECFailure; } #endif /* XXX store encryption cert permanently? */ /* * Remember the current error set because we do not care about * anything set by the functions we are about to call. */ save_error = PORT_GetError(); if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) { attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_SMIME_CAPABILITIES, PR_TRUE); profile = NSS_CMSAttribute_GetValue(attr); attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); stime = NSS_CMSAttribute_GetValue(attr); } rv = CERT_SaveSMimeProfile (cert, profile, stime); if (must_free_cert) CERT_DestroyCertificate(cert); /* * Restore the saved error in case the calls above set a new * one that we do not actually care about. */ PORT_SetError (save_error); return rv; }
/* * NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo * * Just verifies the signature. The assumption is that verification of * the certificate is done already. */ SECStatus NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo, SECItem *digest, /* may be NULL */ SECItem *contentType) /* may be NULL */ { SECKEYPublicKey *publickey = NULL; NSSCMSAttribute *attr; SECItem encoded_attrs; CERTCertificate *cert; NSSCMSVerificationStatus vs = NSSCMSVS_Unverified; PLArenaPool *poolp; SECOidTag digestalgtag; SECOidTag pubkAlgTag; if (signerinfo == NULL) return SECFailure; /* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL ** and cert has not been verified */ cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL); if (cert == NULL) { vs = NSSCMSVS_SigningCertNotFound; goto loser; } if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) { vs = NSSCMSVS_ProcessingError; goto loser; } digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo); pubkAlgTag = SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)); if ((pubkAlgTag == SEC_OID_UNKNOWN) || (digestalgtag == SEC_OID_UNKNOWN)) { vs = NSSCMSVS_SignatureAlgorithmUnknown; goto loser; } if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) { if (contentType) { /* * Check content type * * RFC2630 sez that if there are any authenticated attributes, * then there must be one for content type which matches the * content type of the content being signed, and there must * be one for message digest which matches our message digest. * So check these things first. */ attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE); if (attr == NULL) { vs = NSSCMSVS_MalformedSignature; goto loser; } if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) { vs = NSSCMSVS_MalformedSignature; goto loser; } } /* * Check digest */ attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); if (attr == NULL) { vs = NSSCMSVS_MalformedSignature; goto loser; } if (!digest || NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) { vs = NSSCMSVS_DigestMismatch; goto loser; } if ((poolp = PORT_NewArena (1024)) == NULL) { vs = NSSCMSVS_ProcessingError; goto loser; } /* * Check signature * * The signature is based on a digest of the DER-encoded authenticated * attributes. So, first we encode and then we digest/verify. * we trust the decoder to have the attributes in the right (sorted) * order */ encoded_attrs.data = NULL; encoded_attrs.len = 0; if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL || encoded_attrs.data == NULL || encoded_attrs.len == 0) { PORT_FreeArena(poolp, PR_FALSE); vs = NSSCMSVS_ProcessingError; goto loser; } vs = (VFY_VerifyDataDirect(encoded_attrs.data, encoded_attrs.len, publickey, &(signerinfo->encDigest), pubkAlgTag, digestalgtag, NULL, signerinfo->cmsg->pwfn_arg) != SECSuccess) ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature; PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */ } else { SECItem *sig; /* No authenticated attributes. ** The signature is based on the plain message digest. */ sig = &(signerinfo->encDigest); if (sig->len == 0) goto loser; vs = (!digest || VFY_VerifyDigestDirect(digest, publickey, sig, pubkAlgTag, digestalgtag, signerinfo->cmsg->pwfn_arg) != SECSuccess) ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature; } if (vs == NSSCMSVS_BadSignature) { int error = PORT_GetError(); /* * XXX Change the generic error into our specific one, because * in that case we get a better explanation out of the Security * Advisor. This is really a bug in the PSM error strings (the * "generic" error has a lousy/wrong message associated with it * which assumes the signature verification was done for the * purposes of checking the issuer signature on a certificate) * but this is at least an easy workaround and/or in the * Security Advisor, which specifically checks for the error * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation * in that case but does not similarly check for * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would * probably say the wrong thing in the case that it *was* the * certificate signature check that failed during the cert * verification done above. Our error handling is really a mess. */ if (error == SEC_ERROR_BAD_SIGNATURE) PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); /* * map algorithm failures to NSSCMSVS values */ if ((error == SEC_ERROR_PKCS7_KEYALG_MISMATCH) || (error == SEC_ERROR_INVALID_ALGORITHM)) { /* keep the same error code as 3.11 and before */ PORT_SetError(SEC_ERROR_PKCS7_BAD_SIGNATURE); vs = NSSCMSVS_SignatureAlgorithmUnsupported; } } if (publickey != NULL) SECKEY_DestroyPublicKey (publickey); signerinfo->verificationStatus = vs; return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure; loser: if (publickey != NULL) SECKEY_DestroyPublicKey (publickey); signerinfo->verificationStatus = vs; PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); return SECFailure; }