Canonicalise input in CMS_verify.
[openssl.git] / crypto / cms / cms_env.c
index d2e82906c6c5e35fdd28876b3d10aa47726a2985..8c6d91f3c88143e3c754c9833a26a57688753836 100644 (file)
 /* CMS EnvelopedData Utilities */
 
 DECLARE_ASN1_ITEM(CMS_EnvelopedData)
-DECLARE_ASN1_ITEM(CMS_RecipientInfo)
 DECLARE_ASN1_ITEM(CMS_KeyTransRecipientInfo)
 DECLARE_ASN1_ITEM(CMS_KEKRecipientInfo)
 DECLARE_ASN1_ITEM(CMS_OtherKeyAttribute)
 
 DECLARE_STACK_OF(CMS_RecipientInfo)
 
-static CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms)
+CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms)
        {
        if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_enveloped)
                {
@@ -104,6 +103,40 @@ static CMS_EnvelopedData *cms_enveloped_data_init(CMS_ContentInfo *cms)
        return cms_get0_enveloped(cms);
        }
 
+int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd)
+       {
+       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 (i == -2)
+               {
+               CMSerr(CMS_F_CMS_ENV_ASN1_CTRL,
+                               CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+               return 0;
+               }
+       if (i <= 0)
+               {
+               CMSerr(CMS_F_CMS_ENV_ASN1_CTRL, CMS_R_CTRL_FAILURE);
+               return 0;
+               }
+       return 1;
+       }
+
 STACK_OF(CMS_RecipientInfo) *CMS_get0_RecipientInfos(CMS_ContentInfo *cms)
        {
        CMS_EnvelopedData *env;
@@ -118,6 +151,15 @@ int CMS_RecipientInfo_type(CMS_RecipientInfo *ri)
        return ri->type;
        }
 
+EVP_PKEY_CTX *CMS_RecipientInfo_get0_pkey_ctx(CMS_RecipientInfo *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;
+       }
+
 CMS_ContentInfo *CMS_EnvelopedData_create(const EVP_CIPHER *cipher)
        {
        CMS_ContentInfo *cms;
@@ -139,231 +181,119 @@ CMS_ContentInfo *CMS_EnvelopedData_create(const EVP_CIPHER *cipher)
        return NULL;
        }
 
-/* Add a recipient certificate. For now only handle key transport.
- * If we ever handle key agreement will need updating.
- */
-CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms,
-                                       X509 *recip, unsigned int flags)
-       {
-       CMS_RecipientInfo *ri = NULL;
-       CMS_KeyTransRecipientInfo *ktri;
-       CMS_EnvelopedData *env;
-       EVP_PKEY *pk = NULL;
-       int i, type;
-       env = cms_get0_enveloped(cms);
-       if (!env)
-               goto err;
+/* Key Transport Recipient Info (KTRI) routines */
 
-       /* Initialize recipient info */
-       ri = M_ASN1_new_of(CMS_RecipientInfo);
-       if (!ri)
-               goto merr;
+/* Initialise a ktri based on passed certificate and key */
 
-       /* Initialize and add key transport recipient info */
+static int cms_RecipientInfo_ktri_init(CMS_RecipientInfo *ri, X509 *recip,
+                               EVP_PKEY *pk, unsigned int flags)
+       {
+       CMS_KeyTransRecipientInfo *ktri;
+       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 (pk->ameth && pk->ameth->pkey_ctrl)
-               {
-               i = pk->ameth->pkey_ctrl(pk, ASN1_PKEY_CTRL_CMS_ENVELOPE,
-                                               0, ri);
-               if (i == -2)
-                       {
-                       CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT,
-                               CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
-                       goto err;
-                       }
-               if (i <= 0)
-                       {
-                       CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT,
-                               CMS_R_CTRL_FAILURE);
-                       goto err;
-                       }
-               }
-
-       if (!sk_CMS_RecipientInfo_push(env->recipientInfos, ri))
-               goto merr;
-
-       return ri;
-
-       merr:
-       CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT, ERR_R_MALLOC_FAILURE);
-       err:
-       if (ri)
-               M_ASN1_free_of(ri, CMS_RecipientInfo);
-       return NULL;
-
-       }
-
-int CMS_RecipientInfo_kekri_get0_id(CMS_RecipientInfo *ri,
-                                       X509_ALGOR **palg,
-                                       ASN1_OCTET_STRING **pid,
-                                       ASN1_GENERALIZEDTIME **pdate,
-                                       ASN1_OBJECT **potherid,
-                                       ASN1_TYPE **pothertype)
-       {
-       CMS_KEKIdentifier *rkid;
-       if (ri->type != CMS_RECIPINFO_KEK)
-               {
-               CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID, CMS_R_NOT_KEK);
+       if (!cms_set1_SignerIdentifier(ktri->rid, recip, idtype))
                return 0;
-               }
-       rkid =  ri->d.kekri->kekid;
-       if (palg)
-               *palg = ri->d.kekri->keyEncryptionAlgorithm;
-       if (pid)
-               *pid = rkid->keyIdentifier;
-       if (pdate)
-               *pdate = rkid->date;
-       if (potherid)
-               {
-               if (rkid->other)
-                       *potherid = rkid->other->keyAttrId;
-               else
-                       *potherid = NULL;
-               }
-       if (pothertype)
-               {
-               if (rkid->other)
-                       *pothertype = rkid->other->keyAttr;
-               else
-                       *pothertype = NULL;
-               }
-       return 1;
-       }
 
-/* For now hard code AES key wrap info */
+       CRYPTO_add(&recip->references, 1, CRYPTO_LOCK_X509);
+       CRYPTO_add(&pk->references, 1, CRYPTO_LOCK_EVP_PKEY);
+       ktri->pkey = pk;
+       ktri->recip = recip;
 
-static int aes_wrap_keylen(int nid)
-       {
-       switch (nid)
+       if (flags & CMS_KEY_PARAM)
                {
-               case NID_id_aes128_wrap:
-               return 16;
-
-               case NID_id_aes192_wrap:
-               return  24;
-
-               case NID_id_aes256_wrap:
-               return  32;
-
-               default:
-               return 0;
+               ktri->pctx = EVP_PKEY_CTX_new(ktri->pkey, NULL);
+               if (!ktri->pctx)
+                       return 0;
+               if (EVP_PKEY_encrypt_init(ktri->pctx) <= 0)
+                       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_add0_recipient_key(CMS_ContentInfo *cms, int nid,
-                                       unsigned char *key, size_t keylen,
-                                       unsigned char *id, size_t idlen,
-                                       ASN1_GENERALIZEDTIME *date,
-                                       ASN1_OBJECT *otherTypeId,
-                                       ASN1_TYPE *otherType)
+CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms,
+                                       X509 *recip, unsigned int flags)
        {
        CMS_RecipientInfo *ri = NULL;
        CMS_EnvelopedData *env;
-       CMS_KEKRecipientInfo *kekri;
-       size_t exp_keylen = 0;
+       EVP_PKEY *pk = NULL;
        env = cms_get0_enveloped(cms);
        if (!env)
                goto err;
 
-       exp_keylen = aes_wrap_keylen(nid);
+       /* Initialize recipient info */
+       ri = M_ASN1_new_of(CMS_RecipientInfo);
+       if (!ri)
+               goto merr;
 
-       if (!exp_keylen)
+       pk = X509_get_pubkey(recip);
+       if (!pk)
                {
-               CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY,
-                               CMS_R_UNSUPPORTED_KEK_ALGORITHM);
+               CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT,
+                               CMS_R_ERROR_GETTING_PUBLIC_KEY);
                goto err;
                }
 
-       if (keylen != exp_keylen)
+       switch (cms_pkey_get_ri_type(pk))
                {
-               CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY, CMS_R_INVALID_KEY_LENGTH);
-               goto err;
-               }
 
-       /* Initialize recipient info */
-       ri = M_ASN1_new_of(CMS_RecipientInfo);
-       if (!ri)
-               goto merr;
+               case CMS_RECIPINFO_TRANS:
+               if (!cms_RecipientInfo_ktri_init(ri, recip, pk, flags))
+                       goto err;
+               break;
 
-       ri->d.kekri = M_ASN1_new_of(CMS_KEKRecipientInfo);
-       if (!ri->d.kekri)
-               goto merr;
-       ri->type = CMS_RECIPINFO_KEK;
+               case CMS_RECIPINFO_AGREE:
+               if (!cms_RecipientInfo_kari_init(ri, recip, pk, flags))
+                       goto err;
+               break;
 
-       kekri = ri->d.kekri;
+               default:
+               CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT,
+                                       CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+               goto err;
 
-       if (otherTypeId)
-               {
-               kekri->kekid->other = M_ASN1_new_of(CMS_OtherKeyAttribute);
-               if (kekri->kekid->other == NULL)
-                       goto merr;
                }
 
        if (!sk_CMS_RecipientInfo_push(env->recipientInfos, ri))
                goto merr;
 
-       /* After this point no calls can fail */
-
-       kekri->version = 4;
-
-       kekri->key = key;
-       kekri->keylen = keylen;
-
-       ASN1_STRING_set0(kekri->kekid->keyIdentifier, id, idlen);
-
-       kekri->kekid->date = date;
-
-       kekri->kekid->other->keyAttrId = otherTypeId;
-       kekri->kekid->other->keyAttr = otherType;
-
-       X509_ALGOR_set0(kekri->keyEncryptionAlgorithm,
-                               OBJ_nid2obj(nid), V_ASN1_UNDEF, NULL);
+       EVP_PKEY_free(pk);
 
        return ri;
 
        merr:
-       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY, ERR_R_MALLOC_FAILURE);
+       CMSerr(CMS_F_CMS_ADD1_RECIPIENT_CERT, ERR_R_MALLOC_FAILURE);
        err:
        if (ri)
                M_ASN1_free_of(ri, CMS_RecipientInfo);
+       if (pk)
+               EVP_PKEY_free(pk);
        return NULL;
 
        }
@@ -431,29 +361,6 @@ int CMS_RecipientInfo_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pkey)
        return 1;
        }
 
-int CMS_RecipientInfo_set0_key(CMS_RecipientInfo *ri, 
-                               unsigned char *key, size_t keylen)
-       {
-       CMS_KEKRecipientInfo *kekri;
-       int wrap_nid;
-       if (ri->type != CMS_RECIPINFO_KEK)
-               {
-               CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_KEY, CMS_R_NOT_KEK);
-               return 0;
-               }
-       kekri = ri->d.kekri;
-       wrap_nid = OBJ_obj2nid(kekri->keyEncryptionAlgorithm->algorithm);
-       if (aes_wrap_keylen(wrap_nid) != keylen)
-               {
-               CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_KEY,
-                       CMS_R_INVALID_KEY_LENGTH);
-               return 0;
-               }
-       kekri->key = key;
-       kekri->keylen = keylen;
-       return 1;
-       }
-                       
 /* Encrypt content key in key transport recipient info */
 
 static int cms_RecipientInfo_ktri_encrypt(CMS_ContentInfo *cms,
@@ -461,7 +368,7 @@ static int cms_RecipientInfo_ktri_encrypt(CMS_ContentInfo *cms,
        {
        CMS_KeyTransRecipientInfo *ktri;
        CMS_EncryptedContentInfo *ec;
-       EVP_PKEY_CTX *pctx = NULL;
+       EVP_PKEY_CTX *pctx;
        unsigned char *ek = NULL;
        size_t eklen;
 
@@ -476,12 +383,22 @@ static int cms_RecipientInfo_ktri_encrypt(CMS_ContentInfo *cms,
        ktri = ri->d.ktri;
        ec = cms->d.envelopedData->encryptedContentInfo;
 
-       pctx = EVP_PKEY_CTX_new(ktri->pkey, NULL);
-       if (!pctx)
-               return 0;
+       pctx = ktri->pctx;
 
-       if (EVP_PKEY_encrypt_init(pctx) <= 0)
-               goto err;
+       if (pctx)
+               {
+               if (!cms_env_asn1_ctrl(ri, 0))
+                       goto err;
+               }
+       else
+               {
+               pctx = EVP_PKEY_CTX_new(ktri->pkey, NULL);
+               if (!pctx)
+                       return 0;
+
+               if (EVP_PKEY_encrypt_init(pctx) <= 0)
+                       goto err;
+               }
 
        if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_ENCRYPT,
                                EVP_PKEY_CTRL_CMS_ENCRYPT, 0, ri) <= 0)
@@ -512,21 +429,28 @@ static int cms_RecipientInfo_ktri_encrypt(CMS_ContentInfo *cms,
 
        err:
        if (pctx)
+               {
                EVP_PKEY_CTX_free(pctx);
+               ktri->pctx = NULL;
+               }
        if (ek)
                OPENSSL_free(ek);
        return ret;
 
        }
 
+/* Decrypt content key from KTRI */
+
 static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
                                                        CMS_RecipientInfo *ri)
        {
        CMS_KeyTransRecipientInfo *ktri = ri->d.ktri;
-       EVP_PKEY_CTX *pctx = NULL;
+       EVP_PKEY *pkey = ktri->pkey;
        unsigned char *ek = NULL;
        size_t eklen;
        int ret = 0;
+       CMS_EncryptedContentInfo *ec;
+       ec = cms->d.envelopedData->encryptedContentInfo;
 
        if (ktri->pkey == NULL)
                {
@@ -535,21 +459,24 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
                return 0;
                }
 
-       pctx = EVP_PKEY_CTX_new(ktri->pkey, NULL);
-       if (!pctx)
+       ktri->pctx = EVP_PKEY_CTX_new(pkey, NULL);
+       if (!ktri->pctx)
                return 0;
 
-       if (EVP_PKEY_decrypt_init(pctx) <= 0)
+       if (EVP_PKEY_decrypt_init(ktri->pctx) <= 0)
                goto err;
 
-       if (EVP_PKEY_CTX_ctrl(pctx, -1, EVP_PKEY_OP_DECRYPT,
+       if (!cms_env_asn1_ctrl(ri, 1))
+               goto err;
+
+       if (EVP_PKEY_CTX_ctrl(ktri->pctx, -1, EVP_PKEY_OP_DECRYPT,
                                EVP_PKEY_CTRL_CMS_DECRYPT, 0, ri) <= 0)
                {
                CMSerr(CMS_F_CMS_RECIPIENTINFO_KTRI_DECRYPT, CMS_R_CTRL_ERROR);
                goto err;
                }
 
-       if (EVP_PKEY_decrypt(pctx, NULL, &eklen,
+       if (EVP_PKEY_decrypt(ktri->pctx, NULL, &eklen,
                                ktri->encryptedKey->data,
                                ktri->encryptedKey->length) <= 0)
                goto err;
@@ -563,7 +490,7 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
                goto err;
                }
 
-       if (EVP_PKEY_decrypt(pctx, ek, &eklen,
+       if (EVP_PKEY_decrypt(ktri->pctx, ek, &eklen,
                                ktri->encryptedKey->data,
                                ktri->encryptedKey->length) <= 0)
                {
@@ -573,18 +500,232 @@ static int cms_RecipientInfo_ktri_decrypt(CMS_ContentInfo *cms,
 
        ret = 1;
 
-       cms->d.envelopedData->encryptedContentInfo->key = ek;
-       cms->d.envelopedData->encryptedContentInfo->keylen = eklen;
+       if (ec->key)
+               {
+               OPENSSL_cleanse(ec->key, ec->keylen);
+               OPENSSL_free(ec->key);
+               }
+
+       ec->key = ek;
+       ec->keylen = eklen;
 
        err:
-       if (pctx)
-               EVP_PKEY_CTX_free(pctx);
+       if (ktri->pctx)
+               {
+               EVP_PKEY_CTX_free(ktri->pctx);
+               ktri->pctx = NULL;
+               }
        if (!ret && ek)
                OPENSSL_free(ek);
 
        return ret;
        }
 
+/* Key Encrypted Key (KEK) RecipientInfo routines */
+
+int CMS_RecipientInfo_kekri_id_cmp(CMS_RecipientInfo *ri, 
+                                       const unsigned char *id, size_t idlen)
+       {
+       ASN1_OCTET_STRING tmp_os;
+       CMS_KEKRecipientInfo *kekri;
+       if (ri->type != CMS_RECIPINFO_KEK)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_ID_CMP, CMS_R_NOT_KEK);
+               return -2;
+               }
+       kekri = ri->d.kekri;
+       tmp_os.type = V_ASN1_OCTET_STRING;
+       tmp_os.flags = 0;
+       tmp_os.data = (unsigned char *)id;
+       tmp_os.length = (int)idlen;
+       return ASN1_OCTET_STRING_cmp(&tmp_os, kekri->kekid->keyIdentifier);
+       }
+
+/* For now hard code AES key wrap info */
+
+static size_t aes_wrap_keylen(int nid)
+       {
+       switch (nid)
+               {
+               case NID_id_aes128_wrap:
+               return 16;
+
+               case NID_id_aes192_wrap:
+               return  24;
+
+               case NID_id_aes256_wrap:
+               return  32;
+
+               default:
+               return 0;
+               }
+       }
+
+CMS_RecipientInfo *CMS_add0_recipient_key(CMS_ContentInfo *cms, int nid,
+                                       unsigned char *key, size_t keylen,
+                                       unsigned char *id, size_t idlen,
+                                       ASN1_GENERALIZEDTIME *date,
+                                       ASN1_OBJECT *otherTypeId,
+                                       ASN1_TYPE *otherType)
+       {
+       CMS_RecipientInfo *ri = NULL;
+       CMS_EnvelopedData *env;
+       CMS_KEKRecipientInfo *kekri;
+       env = cms_get0_enveloped(cms);
+       if (!env)
+               goto err;
+
+       if (nid == NID_undef)
+               {
+               switch (keylen)
+                       {
+                       case 16:
+                       nid = NID_id_aes128_wrap;
+                       break;
+
+                       case  24:
+                       nid = NID_id_aes192_wrap;
+                       break;
+
+                       case  32:
+                       nid = NID_id_aes256_wrap;
+                       break;
+
+                       default:
+                       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY,
+                                               CMS_R_INVALID_KEY_LENGTH);
+                       goto err;
+                       }
+
+               }
+       else
+               {
+
+               size_t exp_keylen = aes_wrap_keylen(nid);
+
+               if (!exp_keylen)
+                       {
+                       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY,
+                                       CMS_R_UNSUPPORTED_KEK_ALGORITHM);
+                       goto err;
+                       }
+
+               if (keylen != exp_keylen)
+                       {
+                       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY,
+                                       CMS_R_INVALID_KEY_LENGTH);
+                       goto err;
+                       }
+
+               }
+
+       /* Initialize recipient info */
+       ri = M_ASN1_new_of(CMS_RecipientInfo);
+       if (!ri)
+               goto merr;
+
+       ri->d.kekri = M_ASN1_new_of(CMS_KEKRecipientInfo);
+       if (!ri->d.kekri)
+               goto merr;
+       ri->type = CMS_RECIPINFO_KEK;
+
+       kekri = ri->d.kekri;
+
+       if (otherTypeId)
+               {
+               kekri->kekid->other = M_ASN1_new_of(CMS_OtherKeyAttribute);
+               if (kekri->kekid->other == NULL)
+                       goto merr;
+               }
+
+       if (!sk_CMS_RecipientInfo_push(env->recipientInfos, ri))
+               goto merr;
+
+
+       /* After this point no calls can fail */
+
+       kekri->version = 4;
+
+       kekri->key = key;
+       kekri->keylen = keylen;
+
+       ASN1_STRING_set0(kekri->kekid->keyIdentifier, id, idlen);
+
+       kekri->kekid->date = date;
+
+       if (kekri->kekid->other)
+               {
+               kekri->kekid->other->keyAttrId = otherTypeId;
+               kekri->kekid->other->keyAttr = otherType;
+               }
+
+       X509_ALGOR_set0(kekri->keyEncryptionAlgorithm,
+                               OBJ_nid2obj(nid), V_ASN1_UNDEF, NULL);
+
+       return ri;
+
+       merr:
+       CMSerr(CMS_F_CMS_ADD0_RECIPIENT_KEY, ERR_R_MALLOC_FAILURE);
+       err:
+       if (ri)
+               M_ASN1_free_of(ri, CMS_RecipientInfo);
+       return NULL;
+
+       }
+
+int CMS_RecipientInfo_kekri_get0_id(CMS_RecipientInfo *ri,
+                                       X509_ALGOR **palg,
+                                       ASN1_OCTET_STRING **pid,
+                                       ASN1_GENERALIZEDTIME **pdate,
+                                       ASN1_OBJECT **potherid,
+                                       ASN1_TYPE **pothertype)
+       {
+       CMS_KEKIdentifier *rkid;
+       if (ri->type != CMS_RECIPINFO_KEK)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID, CMS_R_NOT_KEK);
+               return 0;
+               }
+       rkid =  ri->d.kekri->kekid;
+       if (palg)
+               *palg = ri->d.kekri->keyEncryptionAlgorithm;
+       if (pid)
+               *pid = rkid->keyIdentifier;
+       if (pdate)
+               *pdate = rkid->date;
+       if (potherid)
+               {
+               if (rkid->other)
+                       *potherid = rkid->other->keyAttrId;
+               else
+                       *potherid = NULL;
+               }
+       if (pothertype)
+               {
+               if (rkid->other)
+                       *pothertype = rkid->other->keyAttr;
+               else
+                       *pothertype = NULL;
+               }
+       return 1;
+       }
+
+int CMS_RecipientInfo_set0_key(CMS_RecipientInfo *ri, 
+                               unsigned char *key, size_t keylen)
+       {
+       CMS_KEKRecipientInfo *kekri;
+       if (ri->type != CMS_RECIPINFO_KEK)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_SET0_KEY, CMS_R_NOT_KEK);
+               return 0;
+               }
+
+       kekri = ri->d.kekri;
+       kekri->key = key;
+       kekri->keylen = keylen;
+       return 1;
+       }
+
 
 /* Encrypt content key in KEK recipient info */
 
@@ -646,6 +787,8 @@ static int cms_RecipientInfo_kekri_encrypt(CMS_ContentInfo *cms,
 
        }
 
+/* Decrypt content key in KEK recipient info */
+
 static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms,
                                        CMS_RecipientInfo *ri)
        {
@@ -654,7 +797,7 @@ static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms,
        AES_KEY actx;
        unsigned char *ukey = NULL;
        int ukeylen;
-       int r = 0;
+       int r = 0, wrap_nid;
 
        ec = cms->d.envelopedData->encryptedContentInfo;
 
@@ -666,6 +809,14 @@ static int cms_RecipientInfo_kekri_decrypt(CMS_ContentInfo *cms,
                return 0;
                }
 
+       wrap_nid = OBJ_obj2nid(kekri->keyEncryptionAlgorithm->algorithm);
+       if (aes_wrap_keylen(wrap_nid) != kekri->keylen)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT,
+                       CMS_R_INVALID_KEY_LENGTH);
+               return 0;
+               }
+
        /* If encrypted key length is invalid don't bother */
 
        if (kekri->encryptedKey->length < 16)
@@ -727,6 +878,9 @@ int CMS_RecipientInfo_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
                case CMS_RECIPINFO_KEK:
                return cms_RecipientInfo_kekri_decrypt(cms, ri);
 
+               case CMS_RECIPINFO_PASS:
+               return cms_RecipientInfo_pwri_crypt(cms, ri, 0);
+
                default:
                CMSerr(CMS_F_CMS_RECIPIENTINFO_DECRYPT,
                        CMS_R_UNSUPPORTED_RECPIENTINFO_TYPE);
@@ -734,12 +888,109 @@ int CMS_RecipientInfo_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
                }
        }
 
+int CMS_RecipientInfo_encrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
+       {
+       switch (ri->type)
+               {
+               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;
+
+               case CMS_RECIPINFO_PASS:
+               return cms_RecipientInfo_pwri_crypt(cms, ri, 1);
+               break;
+
+               default:
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_ENCRYPT,
+                               CMS_R_UNSUPPORTED_RECIPIENT_TYPE);
+               return 0;
+               }
+       }
+
+/* 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)
+                       {
+                       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;
        STACK_OF(CMS_RecipientInfo) *rinfos;
        CMS_RecipientInfo *ri;
-       int i, r, ok = 0;
+       int i, ok = 0;
        BIO *ret;
 
        /* Get BIO first to set up key */
@@ -752,35 +1003,21 @@ BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms)
        if (!ret || !ec->cipher)
                return ret;
 
+       /* Now encrypt content key according to each RecipientInfo type */
+
        rinfos = cms->d.envelopedData->recipientInfos;
 
        for (i = 0; i < sk_CMS_RecipientInfo_num(rinfos); i++)
                {
                ri = sk_CMS_RecipientInfo_value(rinfos, i);
-
-               switch (ri->type)
-                       {
-                       case CMS_RECIPINFO_TRANS:
-                       r = cms_RecipientInfo_ktri_encrypt(cms, ri);
-                       break;
-
-                       case CMS_RECIPINFO_KEK:
-                       r = cms_RecipientInfo_kekri_encrypt(cms, ri);
-                       break;
-
-                       default:
-                       CMSerr(CMS_F_CMS_ENVELOPEDDATA_INIT_BIO,
-                               CMS_R_UNSUPPORTED_RECIPIENT_TYPE);
-                       goto err;
-                       }
-
-               if (r <= 0)
+               if (CMS_RecipientInfo_encrypt(cms, ri) <= 0)
                        {
                        CMSerr(CMS_F_CMS_ENVELOPEDDATA_INIT_BIO,
                                CMS_R_ERROR_SETTING_RECIPIENTINFO);
                        goto err;
                        }
                }
+       cms_env_set_version(cms->d.envelopedData);
 
        ok = 1;
 
@@ -799,3 +1036,18 @@ BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms)
        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;
+       }