return cms_get0_enveloped(cms);
}
-static int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd)
+int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd)
{
- EVP_PKEY *pkey = ri->d.ktri->pkey;
+ EVP_PKEY *pkey;
int i;
+ if (ri->type == CMS_RECIPINFO_TRANS)
+ pkey = ri->d.ktri->pkey;
+ else if (ri->type == CMS_RECIPINFO_AGREE)
+ {
+ EVP_PKEY_CTX *pctx = ri->d.kari->pctx;
+ if (!pctx)
+ return 0;
+ pkey = EVP_PKEY_CTX_get0_pkey(pctx);
+ if (!pkey)
+ return 0;
+ }
+ else
+ return 0;
if (!pkey->ameth || !pkey->ameth->pkey_ctrl)
return 1;
i = pkey->ameth->pkey_ctrl(pkey, ASN1_PKEY_CTRL_CMS_ENVELOPE, cmd, ri);
{
if (ri->type == CMS_RECIPINFO_TRANS)
return ri->d.ktri->pctx;
+ else if (ri->type == CMS_RECIPINFO_AGREE)
+ return ri->d.kari->pctx;
return NULL;
}
/* Key Transport Recipient Info (KTRI) routines */
-/* Add a recipient certificate. For now only handle key transport.
- * If we ever handle key agreement will need updating.
- */
+/* Initialise a ktri based on passed certificate and key */
-CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms,
- X509 *recip, unsigned int flags)
+static int cms_RecipientInfo_ktri_init(CMS_RecipientInfo *ri, X509 *recip,
+ EVP_PKEY *pk, unsigned int flags)
{
- CMS_RecipientInfo *ri = NULL;
CMS_KeyTransRecipientInfo *ktri;
- CMS_EnvelopedData *env;
- EVP_PKEY *pk = NULL;
- int type;
- env = cms_get0_enveloped(cms);
- if (!env)
- goto err;
-
- /* Initialize recipient info */
- ri = M_ASN1_new_of(CMS_RecipientInfo);
- if (!ri)
- goto merr;
-
- /* Initialize and add key transport recipient info */
+ int idtype;
ri->d.ktri = M_ASN1_new_of(CMS_KeyTransRecipientInfo);
if (!ri->d.ktri)
- goto merr;
+ return 0;
ri->type = CMS_RECIPINFO_TRANS;
ktri = ri->d.ktri;
- X509_check_purpose(recip, -1, -1);
- pk = X509_get_pubkey(recip);
- if (!pk)
- {
- CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT,
- CMS_R_ERROR_GETTING_PUBLIC_KEY);
- goto err;
- }
- CRYPTO_add(&recip->references, 1, CRYPTO_LOCK_X509);
- ktri->pkey = pk;
- ktri->recip = recip;
-
if (flags & CMS_USE_KEYID)
{
ktri->version = 2;
- type = CMS_RECIPINFO_KEYIDENTIFIER;
+ idtype = CMS_RECIPINFO_KEYIDENTIFIER;
}
else
{
ktri->version = 0;
- type = CMS_RECIPINFO_ISSUER_SERIAL;
+ idtype = CMS_RECIPINFO_ISSUER_SERIAL;
}
/* Not a typo: RecipientIdentifier and SignerIdentifier are the
* same structure.
*/
- if (!cms_set1_SignerIdentifier(ktri->rid, recip, type))
- goto err;
+ if (!cms_set1_SignerIdentifier(ktri->rid, recip, idtype))
+ return 0;
+
+ CRYPTO_add(&recip->references, 1, CRYPTO_LOCK_X509);
+ CRYPTO_add(&pk->references, 1, CRYPTO_LOCK_EVP_PKEY);
+ ktri->pkey = pk;
+ ktri->recip = recip;
if (flags & CMS_KEY_PARAM)
{
if (!ktri->pctx)
return 0;
if (EVP_PKEY_encrypt_init(ktri->pctx) <= 0)
- goto err;
+ return 0;
}
else if (!cms_env_asn1_ctrl(ri, 0))
+ return 0;
+ return 1;
+ }
+
+/* Add a recipient certificate using appropriate type of RecipientInfo
+ */
+
+CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms,
+ X509 *recip, unsigned int flags)
+ {
+ CMS_RecipientInfo *ri = NULL;
+ CMS_EnvelopedData *env;
+ EVP_PKEY *pk = NULL;
+ env = cms_get0_enveloped(cms);
+ if (!env)
+ goto err;
+
+ /* Initialize recipient info */
+ ri = M_ASN1_new_of(CMS_RecipientInfo);
+ if (!ri)
+ goto merr;
+
+ pk = X509_get_pubkey(recip);
+ if (!pk)
+ {
+ CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT,
+ CMS_R_ERROR_GETTING_PUBLIC_KEY);
+ goto err;
+ }
+
+ switch (cms_pkey_get_ri_type(pk))
+ {
+
+ case CMS_RECIPINFO_TRANS:
+ if (!cms_RecipientInfo_ktri_init(ri, recip, pk, flags))
+ goto err;
+ break;
+
+ case CMS_RECIPINFO_AGREE:
+ if (!cms_RecipientInfo_kari_init(ri, recip, pk, flags))
+ goto err;
+ break;
+
+ default:
+ CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT,
+ CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
goto err;
+ }
+
if (!sk_CMS_RecipientInfo_push(env->recipientInfos, ri))
goto merr;
+ EVP_PKEY_free(pk);
+
return ri;
merr:
err:
if (ri)
M_ASN1_free_of(ri, CMS_RecipientInfo);
+ if (pk)
+ EVP_PKEY_free(pk);
return NULL;
}
case CMS_RECIPINFO_TRANS:
return cms_RecipientInfo_ktri_encrypt(cms, ri);
+ case CMS_RECIPINFO_AGREE:
+ return cms_RecipientInfo_kari_encrypt(cms, ri);
+
case CMS_RECIPINFO_KEK:
return cms_RecipientInfo_kekri_encrypt(cms, ri);
break;
}
}
+/* Check structures and fixup version numbers (if necessary) */
+
+static void cms_env_set_originfo_version(CMS_EnvelopedData *env)
+ {
+ CMS_OriginatorInfo *org = env->originatorInfo;
+ int i;
+ if (org == NULL)
+ return;
+ for (i = 0; i < sk_CMS_CertificateChoices_num(org->certificates); i++)
+ {
+ CMS_CertificateChoices *cch;
+ cch = sk_CMS_CertificateChoices_value(org->certificates, i);
+ if (cch->type == CMS_CERTCHOICE_OTHER)
+ {
+ env->version = 4;
+ return;
+ }
+ else if (cch->type == CMS_CERTCHOICE_V2ACERT)
+ {
+ if (env->version < 3)
+ env->version = 3;
+ }
+ }
+
+ for (i = 0; i < sk_CMS_RevocationInfoChoice_num(org->crls); i++)
+ {
+ CMS_RevocationInfoChoice *rch;
+ rch = sk_CMS_RevocationInfoChoice_value(org->crls, i);
+ if (rch->type == CMS_REVCHOICE_OTHER)
+ {
+ env->version = 4;
+ return;
+ }
+ }
+ }
+
+static void cms_env_set_version(CMS_EnvelopedData *env)
+ {
+ int i;
+ CMS_RecipientInfo *ri;
+
+ /* Can't set version higher than 4 so if 4 or more already nothing
+ * to do.
+ */
+ if (env->version >= 4)
+ return;
+
+ cms_env_set_originfo_version(env);
+
+ if (env->version >= 3)
+ return;
+
+ for (i = 0; i < sk_CMS_RecipientInfo_num(env->recipientInfos); i++)
+ {
+ ri = sk_CMS_RecipientInfo_value(env->recipientInfos, i);
+ if (ri->type == CMS_RECIPINFO_PASS || ri->type == CMS_RECIPINFO_OTHER)
+ {
+ env->version = 3;
+ return;
+ }
+ else if (ri->type != CMS_RECIPINFO_TRANS || ri->d.ktri->version != 0)
+ {
+ env->version = 2;
+ }
+ }
+ if (env->version == 2)
+ return;
+ if (env->originatorInfo || env->unprotectedAttrs)
+ env->version = 2;
+ env->version = 0;
+ }
+
BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms)
{
CMS_EncryptedContentInfo *ec;
goto err;
}
}
+ cms_env_set_version(cms->d.envelopedData);
ok = 1;
return NULL;
}
+/* Get RecipientInfo type (if any) supported by a key (public or private).
+ * To retain compatibility with previous behaviour if the ctrl value isn't
+ * supported we assume key transport.
+ */
+int cms_pkey_get_ri_type(EVP_PKEY *pk)
+ {
+ if (pk->ameth && pk->ameth->pkey_ctrl)
+ {
+ int i, r;
+ i = pk->ameth->pkey_ctrl(pk, ASN1_PKEY_CTRL_CMS_RI_TYPE, 0, &r);
+ if (i > 0)
+ return r;
+ }
+ return CMS_RECIPINFO_TRANS;
+ }