/* * NSS_CMSEncryptedData_Encode_BeforeStart - do all the necessary things to a EncryptedData * before encoding begins. * * In particular: * - set the correct version value. * - get the encryption key */ SECStatus NSS_CMSEncryptedData_Encode_BeforeStart(NSSCMSEncryptedData *encd) { int version; PK11SymKey *bulkkey = NULL; SECItem *dummy; NSSCMSContentInfo *cinfo = &(encd->contentInfo); if (NSS_CMSArray_IsEmpty((void **)encd->unprotectedAttr)) version = NSS_CMS_ENCRYPTED_DATA_VERSION; else version = NSS_CMS_ENCRYPTED_DATA_VERSION_UPATTR; dummy = SEC_ASN1EncodeInteger (encd->cmsg->poolp, &(encd->version), version); if (dummy == NULL) return SECFailure; /* now get content encryption key (bulk key) by using our cmsg callback */ if (encd->cmsg->decrypt_key_cb) bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, NSS_CMSContentInfo_GetContentEncAlg(cinfo)); if (bulkkey == NULL) return SECFailure; /* store the bulk key in the contentInfo so that the encoder can find it */ NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey); PK11_FreeSymKey (bulkkey); return SECSuccess; }
/* * NSS_CMSMessage_IsSigned - see if message contains a signed submessage * * If the CMS message has a SignedData with a signature (not just a SignedData) * return true; false otherwise. This can/should be called before calling * VerifySignature, which will always indicate failure if no signature is * present, but that does not mean there even was a signature! * Note that the content itself can be empty (detached content was sent * another way); it is the presence of the signature that matters. */ PRBool NSS_CMSMessage_IsSigned(NSSCMSMessage *cmsg) { NSSCMSContentInfo *cinfo; /* walk down the chain of contentinfos */ for (cinfo = &(cmsg->contentInfo); cinfo != NULL; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) { switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) { case SEC_OID_PKCS7_SIGNED_DATA: if (!NSS_CMSArray_IsEmpty((void **)cinfo->content.signedData->signerInfos)) return PR_TRUE; break; default: /* callback here for generic wrappers? */ break; } } return PR_FALSE; }
/* * 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; }