Beispiel #1
0
SECStatus
NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd,
                                NSSCMSSignerInfo *signerinfo)
{
    void *mark;
    SECStatus rv;
    SECOidTag digestalgtag;
    PLArenaPool *poolp;

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

    poolp = sigd->cmsg->poolp;

    mark = PORT_ArenaMark(poolp);

    /* add signerinfo */
    rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
    if (rv != SECSuccess)
        goto loser;

    /*
     * add empty digest
     * Empty because we don't have it yet. Either it gets created during encoding
     * (if the data is present) or has to be set externally.
     * XXX maybe pass it in optionally?
     */
    digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
    rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL);
    if (rv != SECSuccess)
        goto loser;

    /*
     * The last thing to get consistency would be adding the digest.
     */

    PORT_ArenaUnmark(poolp, mark);
    return SECSuccess;

loser:
    PORT_ArenaRelease(poolp, mark);
    return SECFailure;
}
Beispiel #2
0
/*
 * NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData
 *     after all the encapsulated data was passed through the encoder.
 *
 * In detail:
 *  - create the signatures in all the SignerInfos
 *
 * Please note that nothing is done to the Certificates and CRLs in the message - this
 * is entirely the responsibility of our callers.
 */
SECStatus
NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd)
{
    NSSCMSSignerInfo **signerinfos, *signerinfo;
    NSSCMSContentInfo *cinfo;
    SECOidTag digestalgtag;
    SECStatus ret = SECFailure;
    SECStatus rv;
    SECItem *contentType;
    int certcount;
    int i, ci, cli, n, rci, si;
    PLArenaPool *poolp;
    CERTCertificateList *certlist;
    extern const SEC_ASN1Template NSSCMSSignerInfoTemplate[];

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

    poolp = sigd->cmsg->poolp;
    cinfo = &(sigd->contentInfo);

    /* did we have digest calculation going on? */
    if (cinfo->privateInfo && cinfo->privateInfo->digcx) {
        rv = NSS_CMSDigestContext_FinishMultiple(cinfo->privateInfo->digcx, poolp,
                                                 &(sigd->digests));
        /* error has been set by NSS_CMSDigestContext_FinishMultiple */
        cinfo->privateInfo->digcx = NULL;
        if (rv != SECSuccess)
            goto loser;
    }

    signerinfos = sigd->signerInfos;
    certcount = 0;

    /* prepare all the SignerInfos (there may be none) */
    for (i = 0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
        signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);

        /* find correct digest for this signerinfo */
        digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
        if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
            /* oops - digest not found */
            PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
            goto loser;
        }

        /* XXX if our content is anything else but data, we need to force the
         * presence of signed attributes (RFC2630 5.3 "signedAttributes is a
         * collection...") */

        /* pass contentType here as we want a contentType attribute */
        if ((contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo)) == NULL)
            goto loser;

        /* sign the thing */
        rv = NSS_CMSSignerInfo_Sign(signerinfo, sigd->digests[n], contentType);
        if (rv != SECSuccess)
            goto loser;

        /* while we're at it, count number of certs in certLists */
        certlist = NSS_CMSSignerInfo_GetCertList(signerinfo);
        if (certlist)
            certcount += certlist->len;
    }

    /* this is a SET OF, so we need to sort them guys */
    rv = NSS_CMSArray_SortByDER((void **)signerinfos, NSSCMSSignerInfoTemplate, NULL);
    if (rv != SECSuccess)
        goto loser;

    /*
     * now prepare certs & crls
     */

    /* count the rest of the certs */
    if (sigd->certs != NULL) {
        for (ci = 0; sigd->certs[ci] != NULL; ci++)
            certcount++;
    }

    if (sigd->certLists != NULL) {
        for (cli = 0; sigd->certLists[cli] != NULL; cli++)
            certcount += sigd->certLists[cli]->len;
    }

    if (certcount == 0) {
        sigd->rawCerts = NULL;
    } else {
        /*
         * Combine all of the certs and cert chains into rawcerts.
         * Note: certcount is an upper bound; we may not need that many slots
         * but we will allocate anyway to avoid having to do another pass.
         * (The temporary space saving is not worth it.)
         *
         * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
         *  SetOfDERcertficates implementation
         */
        sigd->rawCerts = (SECItem **)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SECItem *));
        if (sigd->rawCerts == NULL)
            return SECFailure;

        /*
         * XXX Want to check for duplicates and not add *any* cert that is
         * already in the set.  This will be more important when we start
         * dealing with larger sets of certs, dual-key certs (signing and
         * encryption), etc.  For the time being we can slide by...
         *
         * XXX ARGH - this NEEDS to be fixed. need to come up with a decent
         *  SetOfDERcertficates implementation
         */
        rci = 0;
        if (signerinfos != NULL) {
            for (si = 0; signerinfos[si] != NULL; si++) {
                signerinfo = signerinfos[si];
                for (ci = 0; ci < signerinfo->certList->len; ci++)
                    sigd->rawCerts[rci++] = &(signerinfo->certList->certs[ci]);
            }
        }

        if (sigd->certs != NULL) {
            for (ci = 0; sigd->certs[ci] != NULL; ci++)
                sigd->rawCerts[rci++] = &(sigd->certs[ci]->derCert);
        }

        if (sigd->certLists != NULL) {
            for (cli = 0; sigd->certLists[cli] != NULL; cli++) {
                for (ci = 0; ci < sigd->certLists[cli]->len; ci++)
                    sigd->rawCerts[rci++] = &(sigd->certLists[cli]->certs[ci]);
            }
        }

        sigd->rawCerts[rci] = NULL;

        /* this is a SET OF, so we need to sort them guys - we have the DER already, though */
        NSS_CMSArray_Sort((void **)sigd->rawCerts, NSS_CMSUtil_DERCompare, NULL, NULL);
    }

    ret = SECSuccess;

loser:
    return ret;
}
Beispiel #3
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;
}
Beispiel #4
0
/*
 * NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData
 *     before start of encoding.
 *
 * In detail:
 *  - find out about the right value to put into sigd->version
 *  - come up with a list of digestAlgorithms (which should be the union of the algorithms
 *         in the signerinfos).
 *         If we happen to have a pre-set list of algorithms (and digest values!), we
 *         check if we have all the signerinfos' algorithms. If not, this is an error.
 */
SECStatus
NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd)
{
    NSSCMSSignerInfo *signerinfo;
    SECOidTag digestalgtag;
    SECItem *dummy;
    int version;
    SECStatus rv;
    PRBool haveDigests = PR_FALSE;
    int n, i;
    PLArenaPool *poolp;

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

    poolp = sigd->cmsg->poolp;

    /* we assume that we have precomputed digests if there is a list of algorithms, and */
    /* a chunk of data for each of those algorithms */
    if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
        for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
            if (sigd->digests[i] == NULL)
                break;
        }
        if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */
            haveDigests = PR_TRUE;             /* yes: we must have all the digests */
    }

    version = NSS_CMS_SIGNED_DATA_VERSION_BASIC;

    /* RFC2630 5.1 "version is the syntax version number..." */
    if (NSS_CMSContentInfo_GetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
        version = NSS_CMS_SIGNED_DATA_VERSION_EXT;

    /* prepare all the SignerInfos (there may be none) */
    for (i = 0; i < NSS_CMSSignedData_SignerInfoCount(sigd); i++) {
        signerinfo = NSS_CMSSignedData_GetSignerInfo(sigd, i);

        /* RFC2630 5.1 "version is the syntax version number..." */
        if (NSS_CMSSignerInfo_GetVersion(signerinfo) != NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN)
            version = NSS_CMS_SIGNED_DATA_VERSION_EXT;

        /* collect digestAlgorithms from SignerInfos */
        /* (we need to know which algorithms we have when the content comes in) */
        /* do not overwrite any existing digestAlgorithms (and digest) */
        digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
        n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
        if (n < 0 && haveDigests) {
            /* oops, there is a digestalg we do not have a digest for */
            /* but we were supposed to have all the digests already... */
            goto loser;
        } else if (n < 0) {
            /* add the digestAlgorithm & a NULL digest */
            rv = NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, NULL);
            if (rv != SECSuccess)
                goto loser;
        } else {
            /* found it, nothing to do */
        }
    }

    dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
    if (dummy == NULL)
        return SECFailure;

    /* this is a SET OF, so we need to sort them guys */
    rv = NSS_CMSArray_SortByDER((void **)sigd->digestAlgorithms,
                                SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
                                (void **)sigd->digests);
    if (rv != SECSuccess)
        return SECFailure;

    return SECSuccess;

loser:
    return SECFailure;
}
Beispiel #5
0
/*
 * NSS_CMSSignerInfo_Sign - sign something
 *
 */
SECStatus
NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, 
                       SECItem *contentType)
{
    CERTCertificate *cert;
    SECKEYPrivateKey *privkey = NULL;
    SECOidTag digestalgtag;
    SECOidTag pubkAlgTag;
    SECItem signature = { 0 };
    SECStatus rv;
    PLArenaPool *poolp, *tmppoolp = NULL;
    SECAlgorithmID *algID, freeAlgID;
    CERTSubjectPublicKeyInfo *spki;

    PORT_Assert (digest != NULL);

    poolp = signerinfo->cmsg->poolp;

    switch (signerinfo->signerIdentifier.identifierType) {
    case NSSCMSSignerID_IssuerSN:
        cert = signerinfo->cert;

        privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg);
        if (privkey == NULL)
	    goto loser;
        algID = &cert->subjectPublicKeyInfo.algorithm;
        break;
    case NSSCMSSignerID_SubjectKeyID:
        privkey = signerinfo->signingKey;
        signerinfo->signingKey = NULL;
        spki = SECKEY_CreateSubjectPublicKeyInfo(signerinfo->pubKey);
        SECKEY_DestroyPublicKey(signerinfo->pubKey);
        signerinfo->pubKey = NULL;
        SECOID_CopyAlgorithmID(NULL, &freeAlgID, &spki->algorithm);
        SECKEY_DestroySubjectPublicKeyInfo(spki); 
        algID = &freeAlgID;
        break;
    default:
        goto loser;
    }
    digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
    /*
     * XXX I think there should be a cert-level interface for this,
     * so that I do not have to know about subjectPublicKeyInfo...
     */
    pubkAlgTag = SECOID_GetAlgorithmTag(algID);
    if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID) {
      SECOID_DestroyAlgorithmID(&freeAlgID, PR_FALSE);
    }

    if (signerinfo->authAttr != NULL) {
	SECOidTag signAlgTag;
	SECItem encoded_attrs;

	/* find and fill in the message digest attribute. */
	rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 
	                       SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
	if (rv != SECSuccess)
	    goto loser;

	if (contentType != NULL) {
	    /* if the caller wants us to, find and fill in the content type attribute. */
	    rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), 
	                    SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
	    if (rv != SECSuccess)
		goto loser;
	}

	if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
	    PORT_SetError(SEC_ERROR_NO_MEMORY);
	    goto loser;
	}

	/*
	 * Before encoding, reorder the attributes so that when they
	 * are encoded, they will be conforming DER, which is required
	 * to have a specific order and that is what must be used for
	 * the hash/signature.  We do this here, rather than building
	 * it into EncodeAttributes, because we do not want to do
	 * such reordering on incoming messages (which also uses
	 * EncodeAttributes) or our old signatures (and other "broken"
	 * implementations) will not verify.  So, we want to guarantee
	 * that we send out good DER encodings of attributes, but not
	 * to expect to receive them.
	 */
	if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
	    goto loser;

	encoded_attrs.data = NULL;
	encoded_attrs.len = 0;
	if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr), 
	                &encoded_attrs) == NULL)
	    goto loser;

	signAlgTag = SEC_GetSignatureAlgorithmOidTag(privkey->keyType, 
                                                     digestalgtag);
	if (signAlgTag == SEC_OID_UNKNOWN) {
	    PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
	    goto loser;
	}

	rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len, 
	                  privkey, signAlgTag);
	PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
	tmppoolp = 0;
    } else {
	rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
    }
    SECKEY_DestroyPrivateKey(privkey);
    privkey = NULL;

    if (rv != SECSuccess)
	goto loser;

    if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) 
          != SECSuccess)
	goto loser;

    SECITEM_FreeItem(&signature, PR_FALSE);

    if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), pubkAlgTag, 
                              NULL) != SECSuccess)
	goto loser;

    return SECSuccess;

loser:
    if (signature.len != 0)
	SECITEM_FreeItem (&signature, PR_FALSE);
    if (privkey)
	SECKEY_DestroyPrivateKey(privkey);
    if (tmppoolp)
	PORT_FreeArena(tmppoolp, PR_FALSE);
    return SECFailure;
}