Beispiel #1
0
SECStatus
NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
			    SECCertUsage certusage)
{
    CERTCertificate *cert;
    PRTime stime;

    if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
	signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
	return SECFailure;
    }

    /*
     * Get and convert the signing time; if available, it will be used
     * both on the cert verification and for importing the sender
     * email profile.
     */
    if (NSS_CMSSignerInfo_GetSigningTime (signerinfo, &stime) != SECSuccess)
	stime = PR_Now(); /* not found or conversion failed, so check against now */
    
    /*
     * XXX  This uses the signing time, if available.  Additionally, we
     * might want to, if there is no signing time, get the message time
     * from the mail header itself, and use that.  That would require
     * a change to our interface though, and for S/MIME callers to pass
     * in a time (and for non-S/MIME callers to pass in nothing, or
     * maybe make them pass in the current time, always?).
     */
    if (CERT_VerifyCert(certdb, cert, PR_TRUE, certusage, stime, 
                        signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
	signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
	return SECFailure;
    }
    return SECSuccess;
}
Beispiel #2
0
/*
 * NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
 *
 * sinfo - signerInfo data for this signer
 *
 * Returns a pointer to allocated memory, which must be freed with PORT_Free.
 * A return value of NULL is an error.
 */
char *
NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
{
    CERTCertificate *signercert;

    /* will fail if cert is not verified */
    if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
	return NULL;

    return (CERT_GetCommonName(&signercert->subject));
}
Beispiel #3
0
/*
 * NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
 *
 * sinfo - signerInfo data for this signer
 *
 * Returns a pointer to allocated memory, which must be freed.
 * A return value of NULL is an error.
 */
char *
NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
{
    CERTCertificate *signercert;

    if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
	return NULL;

    if (!signercert->emailAddr || !signercert->emailAddr[0])
	return NULL;

    return (PORT_Strdup(signercert->emailAddr));
}
Beispiel #4
0
static int
verify1(struct message *m, int n)
{
	SECItem	**digests;
	NSSCMSMessage	*msg;
	PLArenaPool	*poolp;
	SECAlgorithmID	**algids;
	CERTCertDBHandle	*handle;
	int	nlevels, i;
	int	status = 0;
	int	foundsender = 0;
	char	*sender;

	if ((m = getsig(m, n, &msg)) == NULL)
		return 1;
	sender = getsender(m);
	handle = CERT_GetDefaultCertDB();
	nlevels = NSS_CMSMessage_ContentLevelCount(msg);
	for (i = 0; i < nlevels; i++) {
		NSSCMSContentInfo	*content;
		SECOidTag	tag;

		content = NSS_CMSMessage_ContentLevel(msg, i);
		tag = NSS_CMSContentInfo_GetContentTypeTag(content);
		if (tag == SEC_OID_PKCS7_SIGNED_DATA) {
			NSSCMSSignedData	*data;
			int	nsigners, j;

			if ((data = NSS_CMSContentInfo_GetContent(content))
					== NULL) {
				fprintf(stderr, "Signed data missing for "
						"message %d.\n", n);
				status = -1;
				break;
			}
			if (!NSS_CMSSignedData_HasDigests(data)) {
				algids = NSS_CMSSignedData_GetDigestAlgs(data);
				if (getdig(m, n, &digests, &poolp, algids)
						!= OKAY) {
					status = -1;
					break;
				}
				if (NSS_CMSSignedData_SetDigests(data, algids,
							digests)
						!= SECSuccess) {
					fprintf(stderr, "Cannot set digests "
							"for message %d.\n", n);
					status = -1;
					break;
				}
				PORT_FreeArena(poolp, PR_FALSE);
			}
			if (NSS_CMSSignedData_ImportCerts(data, handle,
						certUsageEmailSigner,
						PR_FALSE) != SECSuccess) {
				fprintf(stderr, "Cannot temporarily import "
						"certificates for "
						"message %d.\n", n);
				status = -1;
				break;
			}
			nsigners = NSS_CMSSignedData_SignerInfoCount(data);
			if (nsigners == 0) {
				fprintf(stderr, "Message %d has no signers.\n",
						n);
				status = -1;
				break;
			}
			if (!NSS_CMSSignedData_HasDigests(data)) {
				fprintf(stderr, "Message %d has no digests.\n",
						n);
				status = -1;
				break;
			}
			for (j = 0; j < nsigners; j++) {
				const char	*svs;
				NSSCMSSignerInfo	*info;
				NSSCMSVerificationStatus	vs;
				SECStatus	bad;
				CERTCertificate	*cert;
				const char	*addr;
				int	passed = 0;

				info = NSS_CMSSignedData_GetSignerInfo(data, j);
				cert = NSS_CMSSignerInfo_GetSigningCertificate
					(info, handle);
				bad = NSS_CMSSignedData_VerifySignerInfo(data,
						j, handle,
						certUsageEmailSigner);
				vs = NSS_CMSSignerInfo_GetVerificationStatus
					(info);
				svs = NSS_CMSUtil_VerificationStatusToString
					(vs);
				addr = CERT_GetCertEmailAddress(&cert->subject);
				if (sender != NULL && addr != NULL &&
						asccasecmp(sender, addr) == 0)
					foundsender++;
				else {
					addr = CERT_GetFirstEmailAddress(cert);
					while (sender && addr) {
						if (!asccasecmp(sender, addr)) {
							foundsender++;
							break;
						}
						addr = CERT_GetNextEmailAddress
							(cert, addr);
					}
				}
				if (CERT_VerifyCertNow(handle,
						cert, PR_TRUE,
						certUsageEmailSigner,
						NULL) != SECSuccess)
					fprintf(stderr, "Bad certificate for "
							"signer <%s> of "
							"message %d: %s.\n",
							addr ? addr : "?", n,
							bad_cert_str());
				else
					passed++;
				if (bad)
					fprintf(stderr, "Bad status for "
							"signer <%s> of "
							"message %d: %s.\n",
							addr ? addr : "?",
							n, svs);
				else
					passed++;
				if (passed < 2)
					status = -1;
				else if (status == 0)
					status = 1;
			}
		}
	}
	if (foundsender == 0) {
		if (sender) {
			fprintf(stderr, "Signers of message "
					"%d do not include the sender <%s>\n",
				n, sender);
			status = -1;
		} else
			fprintf(stderr, "Warning: Message %d has no From: "
					"header field.\n", n);
	} else if (status == 1)
		printf("Message %d was verified successfully.\n", n);
	if (status == 0)
		fprintf(stderr, "No verification information found in "
				"message %d.\n", n);
	NSS_CMSMessage_Destroy(msg);
	return status != 1;
}
Beispiel #5
0
enum okay
smime_certsave(struct message *m, int n, FILE *op)
{
	NSSCMSMessage	*msg;
	CERTCertDBHandle	*handle;
	int	nlevels, i, cnt = 0;
	enum okay	ok = OKAY;

	if (nss_init() == STOP)
		return STOP;
	if ((m = getsig(m, n, &msg)) == NULL)
		return 1;
	handle = CERT_GetDefaultCertDB();
	nlevels = NSS_CMSMessage_ContentLevelCount(msg);
	for (i = 0; i < nlevels; i++) {
		NSSCMSContentInfo	*content;
		SECOidTag	tag;

		content = NSS_CMSMessage_ContentLevel(msg, i);
		tag = NSS_CMSContentInfo_GetContentTypeTag(content);
		if (tag == SEC_OID_PKCS7_SIGNED_DATA) {
			NSSCMSSignedData	*data;
			int	nsigners, j;

			if ((data = NSS_CMSContentInfo_GetContent(content))
					== NULL) {
				fprintf(stderr, "Signed data missing for "
						"message %d.\n", n);
				ok = STOP;
				break;
			}
			if (NSS_CMSSignedData_ImportCerts(data, handle,
						certUsageEmailSigner,
						PR_FALSE) != SECSuccess) {
				fprintf(stderr, "Cannot temporarily import "
						"certificates for "
						"message %d.\n", n);
				ok = STOP;
				break;
			}
			nsigners = NSS_CMSSignedData_SignerInfoCount(data);
			if (nsigners == 0) {
				fprintf(stderr, "Message %d has no signers.\n",
						n);
				ok = STOP;
				break;
			}
			for (j = 0; j < nsigners; j++) {
				NSSCMSSignerInfo	*info;
				CERTCertificateList	*list;
				CERTCertificate	*cert;
				int	k;

				info = NSS_CMSSignedData_GetSignerInfo(data, j);
				list = NSS_CMSSignerInfo_GetCertList(info);
				if (list) {
					for (k = 0; k < list->len; k++) {
						cert = (CERTCertificate *)
							&list->certs[k];
						dumpcert(cert, op);
						cnt++;
					}
				}
				cert = NSS_CMSSignerInfo_GetSigningCertificate
					(info, handle);
				if (cert) {
					dumpcert(cert, op);
					cnt++;
				}
			}
		}
	}
	NSS_CMSMessage_Destroy(msg);
	if (cnt == 0) {
		fprintf(stderr, "No certificates found in message %d.\n", n);
		ok = STOP;
	}
	return ok;
}
nsresult
VerifyCMSDetachedSignatureIncludingCertificate(
  const SECItem& buffer, const SECItem& detachedDigest,
  nsresult (*verifyCertificate)(CERTCertificate* cert, void* context,
                                void* pinArg),
  void *verifyCertificateContext, void* pinArg)
{
  // XXX: missing pinArg is tolerated.
  if (NS_WARN_IF(!buffer.data && buffer.len > 0) ||
      NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) ||
      (!verifyCertificate) ||
      NS_WARN_IF(!verifyCertificateContext)) {
    return NS_ERROR_INVALID_ARG;
  }

  ScopedNSSCMSMessage
    cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr,
                                        nullptr, nullptr, nullptr, nullptr,
                                        nullptr));
  if (!cmsMsg) {
    return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
  }

  if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) {
    return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
  }

  NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(cmsMsg.get(), 0);
  if (!cinfo) {
    return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
  }

  // signedData is non-owning
  NSSCMSSignedData* signedData =
    reinterpret_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(cinfo));
  if (!signedData) {
    return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
  }

  // Set digest value.
  if (NSS_CMSSignedData_SetDigestValue(signedData, SEC_OID_SHA1,
                                       const_cast<SECItem*>(&detachedDigest))) {
    return NS_ERROR_CMS_VERIFY_BAD_DIGEST;
  }

  // Parse the certificates into CERTCertificate objects held in memory so
  // verifyCertificate will be able to find them during path building.
  ScopedCERTCertList certs(CERT_NewCertList());
  if (!certs) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  if (signedData->rawCerts) {
    for (size_t i = 0; signedData->rawCerts[i]; ++i) {
      ScopedCERTCertificate
        cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
                                     signedData->rawCerts[i], nullptr, false,
                                     true));
      // Skip certificates that fail to parse
      if (cert) {
        if (CERT_AddCertToListTail(certs.get(), cert.get()) == SECSuccess) {
          cert.forget(); // ownership transfered
        } else {
          return NS_ERROR_OUT_OF_MEMORY;
        }
      }
    }
  }

  // Get the end-entity certificate.
  int numSigners = NSS_CMSSignedData_SignerInfoCount(signedData);
  if (NS_WARN_IF(numSigners != 1)) {
    return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
  }
  // signer is non-owning.
  NSSCMSSignerInfo* signer = NSS_CMSSignedData_GetSignerInfo(signedData, 0);
  if (NS_WARN_IF(!signer)) {
    return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
  }
  CERTCertificate* signerCert =
    NSS_CMSSignerInfo_GetSigningCertificate(signer, CERT_GetDefaultCertDB());
  if (!signerCert) {
    return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
  }

  nsresult rv = verifyCertificate(signerCert, verifyCertificateContext, pinArg);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
  SECOidData* contentTypeOidData =
    SECOID_FindOID(&signedData->contentInfo.contentType);
  if (!contentTypeOidData) {
    return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
  }

  return MapSECStatus(NSS_CMSSignerInfo_Verify(signer,
                         const_cast<SECItem*>(&detachedDigest),
                         &contentTypeOidData->oid));
}
Beispiel #7
0
SECStatus
NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
                              SECCertUsage certusage, PRBool keepcerts)
{
    int certcount;
    CERTCertificate **certArray = NULL;
    CERTCertList *certList = NULL;
    CERTCertListNode *node;
    SECStatus rv;
    SECItem **rawArray;
    int i;
    PRTime now;

    if (!sigd) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    certcount = NSS_CMSArray_Count((void **)sigd->rawCerts);

    /* get the certs in the temp DB */
    rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts,
                          &certArray, PR_FALSE, PR_FALSE, NULL);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* save the certs so they don't get destroyed */
    for (i = 0; i < certcount; i++) {
        CERTCertificate *cert = certArray[i];
        if (cert)
            NSS_CMSSignedData_AddTempCertificate(sigd, cert);
    }

    if (!keepcerts) {
        goto done;
    }

    /* build a CertList for filtering */
    certList = CERT_NewCertList();
    if (certList == NULL) {
        rv = SECFailure;
        goto loser;
    }
    for (i = 0; i < certcount; i++) {
        CERTCertificate *cert = certArray[i];
        if (cert)
            cert = CERT_DupCertificate(cert);
        if (cert)
            CERT_AddCertToListTail(certList, cert);
    }

    /* filter out the certs we don't want */
    rv = CERT_FilterCertListByUsage(certList, certusage, PR_FALSE);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* go down the remaining list of certs and verify that they have
     * valid chains, then import them.
     */
    now = PR_Now();
    for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
         node = CERT_LIST_NEXT(node)) {
        CERTCertificateList *certChain;

        if (CERT_VerifyCert(certdb, node->cert,
                            PR_TRUE, certusage, now, NULL, NULL) != SECSuccess) {
            continue;
        }

        certChain = CERT_CertChainFromCert(node->cert, certusage, PR_FALSE);
        if (!certChain) {
            continue;
        }

        /*
         * CertChain returns an array of SECItems, import expects an array of
         * SECItem pointers. Create the SECItem Pointers from the array of
         * SECItems.
         */
        rawArray = (SECItem **)PORT_Alloc(certChain->len * sizeof(SECItem *));
        if (!rawArray) {
            CERT_DestroyCertificateList(certChain);
            continue;
        }
        for (i = 0; i < certChain->len; i++) {
            rawArray[i] = &certChain->certs[i];
        }
        (void)CERT_ImportCerts(certdb, certusage, certChain->len,
                               rawArray, NULL, keepcerts, PR_FALSE, NULL);
        PORT_Free(rawArray);
        CERT_DestroyCertificateList(certChain);
    }

    rv = SECSuccess;

/* XXX CRL handling */

done:
    if (sigd->signerInfos != NULL) {
        /* fill in all signerinfo's certs */
        for (i = 0; sigd->signerInfos[i] != NULL; i++)
            (void)NSS_CMSSignerInfo_GetSigningCertificate(
                sigd->signerInfos[i], certdb);
    }

loser:
    /* now free everything */
    if (certArray) {
        CERT_DestroyCertArray(certArray, certcount);
    }
    if (certList) {
        CERT_DestroyCertList(certList);
    }

    return rv;
}
Beispiel #8
0
/*
 * 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;
}
Beispiel #9
0
/*
 * 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;
}