Beispiel #1
0
static SecCmsRecipientInfoRef
nss_cmsrecipientinfo_create(SecCmsEnvelopedDataRef envd, SecCmsRecipientIDSelector type,
                            SecCertificateRef cert, SecPublicKeyRef pubKey, 
                            const SecAsn1Item *subjKeyID)
{
    SecCmsRecipientInfoRef ri;
    void *mark;
    SECOidTag certalgtag;
    OSStatus rv = SECSuccess;
    SecCmsRecipientEncryptedKey *rek;
    SecCmsOriginatorIdentifierOrKey *oiok;
    unsigned long version;
    SecAsn1Item * dummy;
    PLArenaPool *poolp;
    const SECAlgorithmID *algid;
    SECAlgorithmID freeAlgID;
    
    SecCmsRecipientIdentifier *rid;

    poolp = envd->contentInfo.cmsg->poolp;

    mark = PORT_ArenaMark(poolp);

    ri = (SecCmsRecipientInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsRecipientInfo));
    if (ri == NULL)
	goto loser;

    ri->envelopedData = envd;

#if USE_CDSA_CRYPTO
    if (type == SecCmsRecipientIDIssuerSN)
    {
	rv = SecCertificateGetAlgorithmID(cert,&algid);
    } else {
	PORT_Assert(pubKey);
	rv = SecKeyGetAlgorithmID(pubKey,&algid);
    }
#else
    ri->cert = CERT_DupCertificate(cert);
    if (ri->cert == NULL)
        goto loser;

    const SecAsn1AlgId *length_data_swapped = (const SecAsn1AlgId *)SecCertificateGetPublicKeyAlgorithm(cert);
    freeAlgID.algorithm.Length = (size_t)length_data_swapped->algorithm.Data;
    freeAlgID.algorithm.Data = (uint8_t *)length_data_swapped->algorithm.Length;
    freeAlgID.parameters.Length = (size_t)length_data_swapped->parameters.Data;
    freeAlgID.parameters.Data = (uint8_t *)length_data_swapped->parameters.Length;
    algid = &freeAlgID;
#endif

    certalgtag = SECOID_GetAlgorithmTag(algid);

    rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier;
    switch (certalgtag) {
    case SEC_OID_PKCS1_RSA_ENCRYPTION:
	ri->recipientInfoType = SecCmsRecipientInfoIDKeyTrans;
	rid->identifierType = type;
	if (type == SecCmsRecipientIDIssuerSN) {
	    rid->id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
	    if (rid->id.issuerAndSN == NULL) {
	      break;
	    }
	} else if (type == SecCmsRecipientIDSubjectKeyID){

	    rid->id.subjectKeyID = PORT_ArenaNew(poolp, SecAsn1Item);
	    if (rid->id.subjectKeyID == NULL) {
		rv = SECFailure;
		PORT_SetError(SEC_ERROR_NO_MEMORY);
		break;
	    } 
	    SECITEM_CopyItem(poolp, rid->id.subjectKeyID, subjKeyID);
	    if (rid->id.subjectKeyID->Data == NULL) {
		rv = SECFailure;
		PORT_SetError(SEC_ERROR_NO_MEMORY);
		break;
	    }

#if 0
            SecCmsKeyTransRecipientInfoEx *riExtra;
	    riExtra = &ri->ri.keyTransRecipientInfoEx;
	    riExtra->version = 0;
	    riExtra->pubKey = SECKEY_CopyPublicKey(pubKey);
	    if (riExtra->pubKey == NULL) {
		rv = SECFailure;
		PORT_SetError(SEC_ERROR_NO_MEMORY);
		break;
	    }
#endif
	} else {
	    PORT_SetError(SEC_ERROR_INVALID_ARGS);
	    rv = SECFailure;
	}
	break;
    case SEC_OID_MISSI_KEA_DSS_OLD:
    case SEC_OID_MISSI_KEA_DSS:
    case SEC_OID_MISSI_KEA:
        PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
	if (type == SecCmsRecipientIDSubjectKeyID) {
	    rv = SECFailure;
	    break;
	}
	/* backward compatibility - this is not really a keytrans operation */
	ri->recipientInfoType = SecCmsRecipientInfoIDKeyTrans;
	/* hardcoded issuerSN choice for now */
	ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType = SecCmsRecipientIDIssuerSN;
	ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
	if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
	    rv = SECFailure;
	    break;
	}
	break;
    case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
        PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
	if (type == SecCmsRecipientIDSubjectKeyID) {
	    rv = SECFailure;
	    break;
	}
	/* a key agreement op */
	ri->recipientInfoType = SecCmsRecipientInfoIDKeyAgree;

	if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
	    rv = SECFailure;
	    break;
	}
	/* we do not support the case where multiple recipients 
	 * share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys
	 * in this case, we would need to walk all the recipientInfos, take the
	 * ones that do KeyAgreement algorithms and join them, algorithm by algorithm
	 * Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */

	/* only epheremal-static Diffie-Hellman is supported for now
	 * this is the only form of key agreement that provides potential anonymity
	 * of the sender, plus we do not have to include certs in the message */

	/* force single recipientEncryptedKey for now */
	if ((rek = SecCmsRecipientEncryptedKeyCreate(poolp)) == NULL) {
	    rv = SECFailure;
	    break;
	}

	/* hardcoded IssuerSN choice for now */
	rek->recipientIdentifier.identifierType = SecCmsKeyAgreeRecipientIDIssuerSN;
	if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
	    rv = SECFailure;
	    break;
	}

	oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);

	/* see RFC2630 12.3.1.1 */
	oiok->identifierType = SecCmsOriginatorIDOrKeyOriginatorPublicKey;

	rv = SecCmsArrayAdd(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
				    (void *)rek);

	break;
    default:
	/* other algorithms not supported yet */
	/* NOTE that we do not support any KEK algorithm */
	PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
	rv = SECFailure;
	break;
    }

    if (rv == SECFailure)
	goto loser;

    /* set version */
    switch (ri->recipientInfoType) {
    case SecCmsRecipientInfoIDKeyTrans:
	if (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType == SecCmsRecipientIDIssuerSN)
	    version = SEC_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN;
	else
	    version = SEC_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY;
	dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyTransRecipientInfo.version), version);
	if (dummy == NULL)
	    goto loser;
	break;
    case SecCmsRecipientInfoIDKeyAgree:
	dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyAgreeRecipientInfo.version),
						SEC_CMS_KEYAGREE_RECIPIENT_INFO_VERSION);
	if (dummy == NULL)
	    goto loser;
	break;
    case SecCmsRecipientInfoIDKEK:
	/* NOTE: this cannot happen as long as we do not support any KEK algorithm */
	dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version),
						SEC_CMS_KEK_RECIPIENT_INFO_VERSION);
	if (dummy == NULL)
	    goto loser;
	break;
    
    }

    if (SecCmsEnvelopedDataAddRecipient(envd, ri))
	goto loser;

    PORT_ArenaUnmark (poolp, mark);
#if 0
    if (freeSpki)
      SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
#endif
    return ri;

loser:
#if 0
    if (freeSpki)
      SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
#endif
    PORT_ArenaRelease (poolp, mark);
    return NULL;
}
/* 
 * Set up a SecCmsMessageRef for a EnvelopedData creation.
 */
static OSStatus cmsSetupForEnvelopedData(
	CMSEncoderRef		cmsEncoder)
{
	ASSERT(cmsEncoder->op == EO_Encrypt);
	ASSERT(cmsEncoder->recipients != NULL);
	
    SecCmsContentInfoRef contentInfo = NULL;
    SecCmsEnvelopedDataRef envelopedData = NULL;
	SECOidTag algorithmTag;
    int keySize;
	OSStatus ortn;

	/*
	 * Find encryption algorithm...unfortunately we need a NULL-terminated array
	 * of SecCertificateRefs for this.
	 */
	CFIndex numCerts = CFArrayGetCount(cmsEncoder->recipients);
	CFIndex dex;
	SecCertificateRef *certArray = (SecCertificateRef *)malloc(
		(numCerts+1) * sizeof(SecCertificateRef));

	for(dex=0; dex<numCerts; dex++) {
		certArray[dex] = (SecCertificateRef)CFArrayGetValueAtIndex(
			cmsEncoder->recipients, dex);
	}
	certArray[numCerts] = NULL;
	ortn = SecSMIMEFindBulkAlgForRecipients(certArray, &algorithmTag, &keySize);
	free(certArray);
	if(ortn) {
		CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn);
		return ortn;
	}
	
    /* build chain of objects: message->envelopedData->data */
	if(cmsEncoder->cmsMsg != NULL) {
		SecCmsMessageDestroy(cmsEncoder->cmsMsg);
	}
	cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
	if(cmsEncoder->cmsMsg == NULL) {
		return errSecInternalComponent;
	}
	envelopedData = SecCmsEnvelopedDataCreate(cmsEncoder->cmsMsg, 
		algorithmTag, keySize);
	if(envelopedData == NULL) {
		return errSecInternalComponent;
	}
	contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
	ortn = SecCmsContentInfoSetContentEnvelopedData(cmsEncoder->cmsMsg, 
		contentInfo, envelopedData);
	if(ortn) {
		ortn = cmsRtnToOSStatus(ortn);
		CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn);
		return ortn;
	}
    contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
	if(cmsEncoder->eContentType.Data != NULL) {
		/* Override the default ContentType of id-data */
		ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg, 
			contentInfo, 
			NULL,		/* data - provided to encoder, not here */
			FALSE,		/* detachedContent */
			&cmsEncoder->eContentType);
	}
	else {
		ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg, 
			contentInfo, 
			NULL /* data - provided to encoder, not here */, 
			cmsEncoder->detachedContent);
	}
	if(ortn) {
		ortn = cmsRtnToOSStatus(ortn);
		CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn);
		return ortn;
	}

    /* 
     * create & attach recipient information, one for each recipient
     */
	for(dex=0; dex<numCerts; dex++) {
		SecCmsRecipientInfoRef recipientInfo = NULL;
		
		SecCertificateRef thisRecip = (SecCertificateRef)CFArrayGetValueAtIndex(
			cmsEncoder->recipients, dex);
		recipientInfo = SecCmsRecipientInfoCreate(cmsEncoder->cmsMsg, thisRecip);
		ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
		if(ortn) {
			ortn = cmsRtnToOSStatus(ortn);
			CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn);
			return ortn;
		}
	}
	return errSecSuccess;
}