Canonicalise input in CMS_verify.
[openssl.git] / crypto / cms / cms_env.c
index c26dd5973863c553408f4dec3fa9d38ba89cd338..8c6d91f3c88143e3c754c9833a26a57688753836 100644 (file)
@@ -103,10 +103,23 @@ static CMS_EnvelopedData *cms_enveloped_data_init(CMS_ContentInfo *cms)
        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);
@@ -142,6 +155,8 @@ 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;
        }
 
@@ -168,65 +183,43 @@ CMS_ContentInfo *CMS_EnvelopedData_create(const EVP_CIPHER *cipher)
 
 /* 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)
                {
@@ -234,14 +227,64 @@ CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms,
                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:
@@ -249,6 +292,8 @@ CMS_RecipientInfo *CMS_add1_recipient_cert(CMS_ContentInfo *cms,
        err:
        if (ri)
                M_ASN1_free_of(ri, CMS_RecipientInfo);
+       if (pk)
+               EVP_PKEY_free(pk);
        return NULL;
 
        }
@@ -850,6 +895,9 @@ int CMS_RecipientInfo_encrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
                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;
@@ -865,6 +913,78 @@ int CMS_RecipientInfo_encrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
                }
        }
 
+/* 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;
@@ -897,6 +1017,7 @@ BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms)
                        goto err;
                        }
                }
+       cms_env_set_version(cms->d.envelopedData);
 
        ok = 1;
 
@@ -915,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;
+       }