CMS support for key agreeement recipient info.
authorDr. Stephen Henson <steve@openssl.org>
Wed, 17 Jul 2013 13:36:39 +0000 (14:36 +0100)
committerDr. Stephen Henson <steve@openssl.org>
Wed, 17 Jul 2013 20:45:00 +0000 (21:45 +0100)
Add hooks to support key agreement recipient info type (KARI) using
algorithm specific code in the relevant public key ASN1 method.

12 files changed:
crypto/cms/Makefile
crypto/cms/cms.h
crypto/cms/cms_asn1.c
crypto/cms/cms_env.c
crypto/cms/cms_err.c
crypto/cms/cms_kari.c [new file with mode: 0644]
crypto/cms/cms_lcl.h
crypto/cms/cms_lib.c
crypto/cms/cms_sd.c
crypto/cms/cms_smime.c
crypto/evp/evp.h
crypto/stack/safestack.h

index 1cbb5d0..6f22c82 100644 (file)
@@ -19,10 +19,10 @@ APPS=
 LIB=$(TOP)/libcrypto.a
 LIBSRC= cms_lib.c cms_asn1.c cms_att.c cms_io.c cms_smime.c cms_err.c \
        cms_sd.c cms_dd.c cms_cd.c cms_env.c cms_enc.c cms_ess.c \
-       cms_pwri.c
+       cms_pwri.c cms_kari.c
 LIBOBJ= cms_lib.o cms_asn1.o cms_att.o cms_io.o cms_smime.o cms_err.o \
        cms_sd.o cms_dd.o cms_cd.o cms_env.o cms_enc.o cms_ess.o \
-       cms_pwri.o
+       cms_pwri.o cms_kari.o
 
 SRC= $(LIBSRC)
 
index 1624b79..f644cbf 100644 (file)
@@ -73,9 +73,12 @@ typedef struct CMS_RevocationInfoChoice_st CMS_RevocationInfoChoice;
 typedef struct CMS_RecipientInfo_st CMS_RecipientInfo;
 typedef struct CMS_ReceiptRequest_st CMS_ReceiptRequest;
 typedef struct CMS_Receipt_st CMS_Receipt;
+typedef struct CMS_RecipientEncryptedKey_st CMS_RecipientEncryptedKey;
+typedef struct CMS_OtherKeyAttribute_st CMS_OtherKeyAttribute;
 
 DECLARE_STACK_OF(CMS_SignerInfo)
 DECLARE_STACK_OF(GENERAL_NAMES)
+DECLARE_STACK_OF(CMS_RecipientEncryptedKey)
 DECLARE_ASN1_FUNCTIONS(CMS_ContentInfo)
 DECLARE_ASN1_FUNCTIONS(CMS_ReceiptRequest)
 DECLARE_ASN1_PRINT_FUNCTION(CMS_ContentInfo)
@@ -83,6 +86,7 @@ DECLARE_ASN1_PRINT_FUNCTION(CMS_ContentInfo)
 #define CMS_SIGNERINFO_ISSUER_SERIAL   0
 #define CMS_SIGNERINFO_KEYIDENTIFIER   1
 
+#define CMS_RECIPINFO_NONE             -1
 #define CMS_RECIPINFO_TRANS            0
 #define CMS_RECIPINFO_AGREE            1
 #define CMS_RECIPINFO_KEK              2
@@ -333,8 +337,32 @@ void CMS_ReceiptRequest_get0_values(CMS_ReceiptRequest *rr,
                                        int *pallorfirst,
                                        STACK_OF(GENERAL_NAMES) **plist,
                                        STACK_OF(GENERAL_NAMES) **prto);
-
 #endif
+int CMS_RecipientInfo_kari_get0_alg(CMS_RecipientInfo *ri,
+                                       X509_ALGOR **palg,
+                                       ASN1_OCTET_STRING **pukm);
+STACK_OF(CMS_RecipientEncryptedKey) *
+       CMS_RecipientInfo_kari_get0_reks(CMS_RecipientInfo *ri);
+
+int CMS_RecipientInfo_kari_get0_orig_id(CMS_RecipientInfo *ri,
+                                       X509_ALGOR **pubalg,
+                                       ASN1_BIT_STRING **pubkey,
+                                       ASN1_OCTET_STRING **keyid,
+                                       X509_NAME **issuer, ASN1_INTEGER **sno);
+
+int CMS_RecipientInfo_kari_orig_id_cmp(CMS_RecipientInfo *ri, X509 *cert);
+       
+int CMS_RecipientEncryptedKey_get0_id(CMS_RecipientEncryptedKey *rek,
+                                       ASN1_OCTET_STRING **keyid,
+                                       ASN1_GENERALIZEDTIME **tm,
+                                       CMS_OtherKeyAttribute **other,
+                                       X509_NAME **issuer, ASN1_INTEGER **sno);
+int CMS_RecipientEncryptedKey_cert_cmp(CMS_RecipientEncryptedKey *rek,
+                                               X509 *cert);
+int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk);
+EVP_CIPHER_CTX *CMS_RecipientInfo_kari_get0_ctx(CMS_RecipientInfo *ri);
+int CMS_RecipientInfo_kari_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
+                                       CMS_RecipientEncryptedKey *rek);
 
 /* BEGIN ERROR CODES */
 /* The following lines are auto generated by the script mkerr.pl. Any changes
@@ -391,6 +419,11 @@ void ERR_load_CMS_strings(void);
 #define CMS_F_CMS_RECEIPT_VERIFY                        160
 #define CMS_F_CMS_RECIPIENTINFO_DECRYPT                         134
 #define CMS_F_CMS_RECIPIENTINFO_ENCRYPT                         169
+#define CMS_F_CMS_RECIPIENTINFO_KARI_ENCRYPT            178
+#define CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ALG           175
+#define CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ORIG_ID       173
+#define CMS_F_CMS_RECIPIENTINFO_KARI_GET0_REKS          172
+#define CMS_F_CMS_RECIPIENTINFO_KARI_ORIG_ID_CMP        174
 #define CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT           135
 #define CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT           136
 #define CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID           137
@@ -405,6 +438,8 @@ void ERR_load_CMS_strings(void);
 #define CMS_F_CMS_RECIPIENTINFO_SET0_PASSWORD           168
 #define CMS_F_CMS_RECIPIENTINFO_SET0_PKEY               145
 #define CMS_F_CMS_SD_ASN1_CTRL                          170
+#define CMS_F_CMS_SET1_IAS                              176
+#define CMS_F_CMS_SET1_KEYID                            177
 #define CMS_F_CMS_SET1_SIGNERIDENTIFIER                         146
 #define CMS_F_CMS_SET_DETACHED                          147
 #define CMS_F_CMS_SIGN                                  148
@@ -456,6 +491,7 @@ void ERR_load_CMS_strings(void);
 #define CMS_R_NOT_A_SIGNED_RECEIPT                      165
 #define CMS_R_NOT_ENCRYPTED_DATA                        122
 #define CMS_R_NOT_KEK                                   123
+#define CMS_R_NOT_KEY_AGREEMENT                                 181
 #define CMS_R_NOT_KEY_TRANSPORT                                 124
 #define CMS_R_NOT_PWRI                                  177
 #define CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE           125
index 6a692cd..7d79db2 100644 (file)
@@ -166,10 +166,22 @@ ASN1_CHOICE(CMS_KeyAgreeRecipientIdentifier) = {
   ASN1_IMP(CMS_KeyAgreeRecipientIdentifier, d.rKeyId, CMS_RecipientKeyIdentifier, 0)
 } ASN1_CHOICE_END(CMS_KeyAgreeRecipientIdentifier)
 
-ASN1_SEQUENCE(CMS_RecipientEncryptedKey) = {
+static int cms_rek_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                                                       void *exarg)
+       {
+       CMS_RecipientEncryptedKey *rek = (CMS_RecipientEncryptedKey *)*pval;
+       if(operation == ASN1_OP_FREE_POST)
+               {
+               if (rek->pkey)
+                       EVP_PKEY_free(rek->pkey);
+               }
+       return 1;
+       }
+
+ASN1_SEQUENCE_cb(CMS_RecipientEncryptedKey, cms_rek_cb) = {
        ASN1_SIMPLE(CMS_RecipientEncryptedKey, rid, CMS_KeyAgreeRecipientIdentifier),
        ASN1_SIMPLE(CMS_RecipientEncryptedKey, encryptedKey, ASN1_OCTET_STRING)
-} ASN1_SEQUENCE_END(CMS_RecipientEncryptedKey)
+} ASN1_SEQUENCE_END_cb(CMS_RecipientEncryptedKey, CMS_RecipientEncryptedKey)
 
 ASN1_SEQUENCE(CMS_OriginatorPublicKey) = {
   ASN1_SIMPLE(CMS_OriginatorPublicKey, algorithm, X509_ALGOR),
@@ -182,13 +194,33 @@ ASN1_CHOICE(CMS_OriginatorIdentifierOrKey) = {
   ASN1_IMP(CMS_OriginatorIdentifierOrKey, d.originatorKey, CMS_OriginatorPublicKey, 1)
 } ASN1_CHOICE_END(CMS_OriginatorIdentifierOrKey)
 
-ASN1_SEQUENCE(CMS_KeyAgreeRecipientInfo) = {
+static int cms_kari_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
+                                                       void *exarg)
+       {
+       CMS_KeyAgreeRecipientInfo *kari = (CMS_KeyAgreeRecipientInfo *)*pval;
+       if(operation == ASN1_OP_NEW_POST)
+               {
+               EVP_CIPHER_CTX_init(&kari->ctx);
+               EVP_CIPHER_CTX_set_flags(&kari->ctx,
+                                       EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
+               kari->pctx = NULL;
+               }
+       else if(operation == ASN1_OP_FREE_POST)
+               {
+               if (kari->pctx)
+                       EVP_PKEY_CTX_free(kari->pctx);
+               EVP_CIPHER_CTX_cleanup(&kari->ctx);
+               }
+       return 1;
+       }
+
+ASN1_SEQUENCE_cb(CMS_KeyAgreeRecipientInfo, cms_kari_cb) = {
        ASN1_SIMPLE(CMS_KeyAgreeRecipientInfo, version, LONG),
        ASN1_EXP(CMS_KeyAgreeRecipientInfo, originator, CMS_OriginatorIdentifierOrKey, 0),
        ASN1_EXP_OPT(CMS_KeyAgreeRecipientInfo, ukm, ASN1_OCTET_STRING, 1),
        ASN1_SIMPLE(CMS_KeyAgreeRecipientInfo, keyEncryptionAlgorithm, X509_ALGOR),
        ASN1_SEQUENCE_OF(CMS_KeyAgreeRecipientInfo, recipientEncryptedKeys, CMS_RecipientEncryptedKey)
-} ASN1_SEQUENCE_END(CMS_KeyAgreeRecipientInfo)
+} ASN1_SEQUENCE_END_cb(CMS_KeyAgreeRecipientInfo, CMS_KeyAgreeRecipientInfo)
 
 ASN1_SEQUENCE(CMS_KEKIdentifier) = {
        ASN1_SIMPLE(CMS_KEKIdentifier, keyIdentifier, ASN1_OCTET_STRING),
index cef825b..8c6d91f 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;
@@ -988,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;
+       }
index 35aca59..5d9f685 100644 (file)
@@ -103,7 +103,7 @@ static ERR_STRING_DATA CMS_str_functs[]=
 {ERR_FUNC(CMS_F_CMS_ENVELOPEDDATA_CREATE),     "CMS_EnvelopedData_create"},
 {ERR_FUNC(CMS_F_CMS_ENVELOPEDDATA_INIT_BIO),   "cms_EnvelopedData_init_bio"},
 {ERR_FUNC(CMS_F_CMS_ENVELOPED_DATA_INIT),      "CMS_ENVELOPED_DATA_INIT"},
-{ERR_FUNC(CMS_F_CMS_ENV_ASN1_CTRL),    "CMS_ENV_ASN1_CTRL"},
+{ERR_FUNC(CMS_F_CMS_ENV_ASN1_CTRL),    "cms_env_asn1_ctrl"},
 {ERR_FUNC(CMS_F_CMS_FINAL),    "CMS_final"},
 {ERR_FUNC(CMS_F_CMS_GET0_CERTIFICATE_CHOICES), "CMS_GET0_CERTIFICATE_CHOICES"},
 {ERR_FUNC(CMS_F_CMS_GET0_CONTENT),     "CMS_get0_content"},
@@ -116,6 +116,11 @@ static ERR_STRING_DATA CMS_str_functs[]=
 {ERR_FUNC(CMS_F_CMS_RECEIPT_VERIFY),   "cms_Receipt_verify"},
 {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_DECRYPT),    "CMS_RecipientInfo_decrypt"},
 {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_ENCRYPT),    "CMS_RecipientInfo_encrypt"},
+{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_ENCRYPT),       "cms_RecipientInfo_kari_encrypt"},
+{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ALG),      "CMS_RecipientInfo_kari_get0_alg"},
+{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ORIG_ID),  "CMS_RecipientInfo_kari_get0_orig_id"},
+{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_REKS),     "CMS_RecipientInfo_kari_get0_reks"},
+{ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KARI_ORIG_ID_CMP),   "CMS_RecipientInfo_kari_orig_id_cmp"},
 {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_DECRYPT),      "CMS_RECIPIENTINFO_KEKRI_DECRYPT"},
 {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_ENCRYPT),      "CMS_RECIPIENTINFO_KEKRI_ENCRYPT"},
 {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_KEKRI_GET0_ID),      "CMS_RecipientInfo_kekri_get0_id"},
@@ -130,6 +135,8 @@ static ERR_STRING_DATA CMS_str_functs[]=
 {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_PASSWORD),      "CMS_RecipientInfo_set0_password"},
 {ERR_FUNC(CMS_F_CMS_RECIPIENTINFO_SET0_PKEY),  "CMS_RecipientInfo_set0_pkey"},
 {ERR_FUNC(CMS_F_CMS_SD_ASN1_CTRL),     "CMS_SD_ASN1_CTRL"},
+{ERR_FUNC(CMS_F_CMS_SET1_IAS), "cms_set1_ias"},
+{ERR_FUNC(CMS_F_CMS_SET1_KEYID),       "cms_set1_keyid"},
 {ERR_FUNC(CMS_F_CMS_SET1_SIGNERIDENTIFIER),    "cms_set1_SignerIdentifier"},
 {ERR_FUNC(CMS_F_CMS_SET_DETACHED),     "CMS_set_detached"},
 {ERR_FUNC(CMS_F_CMS_SIGN),     "CMS_sign"},
@@ -184,6 +191,7 @@ static ERR_STRING_DATA CMS_str_reasons[]=
 {ERR_REASON(CMS_R_NOT_A_SIGNED_RECEIPT)  ,"not a signed receipt"},
 {ERR_REASON(CMS_R_NOT_ENCRYPTED_DATA)    ,"not encrypted data"},
 {ERR_REASON(CMS_R_NOT_KEK)               ,"not kek"},
+{ERR_REASON(CMS_R_NOT_KEY_AGREEMENT)     ,"not key agreement"},
 {ERR_REASON(CMS_R_NOT_KEY_TRANSPORT)     ,"not key transport"},
 {ERR_REASON(CMS_R_NOT_PWRI)              ,"not pwri"},
 {ERR_REASON(CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE),"not supported for this key type"},
diff --git a/crypto/cms/cms_kari.c b/crypto/cms/cms_kari.c
new file mode 100644 (file)
index 0000000..2451f0f
--- /dev/null
@@ -0,0 +1,480 @@
+/* crypto/cms/cms_kari.c */
+/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
+ * project.
+ */
+/* ====================================================================
+ * Copyright (c) 2013 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ */
+
+#include "cryptlib.h"
+#include <openssl/asn1t.h>
+#include <openssl/pem.h>
+#include <openssl/x509v3.h>
+#include <openssl/err.h>
+#include <openssl/cms.h>
+#include <openssl/rand.h>
+#include <openssl/aes.h>
+#include "cms_lcl.h"
+#include "asn1_locl.h"
+
+DECLARE_ASN1_ITEM(CMS_KeyAgreeRecipientInfo)
+DECLARE_ASN1_ITEM(CMS_RecipientEncryptedKey)
+DECLARE_ASN1_ITEM(CMS_OriginatorPublicKey)
+
+/* Key Agreement Recipient Info (KARI) routines */
+
+int CMS_RecipientInfo_kari_get0_alg(CMS_RecipientInfo *ri,
+                                       X509_ALGOR **palg,
+                                       ASN1_OCTET_STRING **pukm)
+       {
+       if (ri->type != CMS_RECIPINFO_AGREE)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ALG,
+                       CMS_R_NOT_KEY_AGREEMENT);
+               return 0;
+               }
+       if (palg)
+               *palg = ri->d.kari->keyEncryptionAlgorithm;
+       if (pukm)
+               *pukm = ri->d.kari->ukm;
+       return 1;
+       }
+
+/* Retrieve recipient encrypted keys from a kari */
+
+STACK_OF(CMS_RecipientEncryptedKey) *CMS_RecipientInfo_kari_get0_reks(CMS_RecipientInfo *ri)
+       {
+       if (ri->type != CMS_RECIPINFO_AGREE)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_REKS,
+                       CMS_R_NOT_KEY_AGREEMENT);
+               return NULL;
+               }
+       return ri->d.kari->recipientEncryptedKeys;
+       }
+
+int CMS_RecipientInfo_kari_get0_orig_id(CMS_RecipientInfo *ri,
+                                       X509_ALGOR **pubalg,
+                                       ASN1_BIT_STRING **pubkey,
+                                       ASN1_OCTET_STRING **keyid,
+                                       X509_NAME **issuer, ASN1_INTEGER **sno)
+       {
+       CMS_OriginatorIdentifierOrKey *oik;
+       if (ri->type != CMS_RECIPINFO_AGREE)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_GET0_ORIG_ID,
+                       CMS_R_NOT_KEY_AGREEMENT);
+               return 0;
+               }
+       oik = ri->d.kari->originator;
+       if (issuer)
+               *issuer = NULL;
+       if (sno)
+               *sno = NULL;
+       if (keyid)
+               *keyid = NULL;
+       if (pubalg)
+               *pubalg = NULL;
+       if (pubkey)
+               *pubkey = NULL;
+       if (oik->type == CMS_OIK_ISSUER_SERIAL)
+               {
+               if (issuer)
+                       *issuer = oik->d.issuerAndSerialNumber->issuer;
+               if (sno)
+                       *sno = oik->d.issuerAndSerialNumber->serialNumber;
+               }
+       else if (oik->type == CMS_OIK_KEYIDENTIFIER)
+               {
+               if (keyid)
+                       *keyid = oik->d.subjectKeyIdentifier;
+               }
+       else if (oik->type == CMS_OIK_PUBKEY)
+               {
+               if (pubalg)
+                       *pubalg = oik->d.originatorKey->algorithm;
+               if (pubkey)
+                       *pubkey = oik->d.originatorKey->publicKey;
+               }
+       else
+               return 0;
+       return 1;
+       }
+
+int CMS_RecipientInfo_kari_orig_id_cmp(CMS_RecipientInfo *ri, X509 *cert)
+       {
+       CMS_OriginatorIdentifierOrKey *oik;
+       if (ri->type != CMS_RECIPINFO_AGREE)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_ORIG_ID_CMP,
+                       CMS_R_NOT_KEY_AGREEMENT);
+               return -2;
+               }
+       oik = ri->d.kari->originator;
+       if (oik->type == CMS_OIK_ISSUER_SERIAL)
+               return cms_ias_cert_cmp(oik->d.issuerAndSerialNumber, cert);
+       else if (oik->type == CMS_OIK_KEYIDENTIFIER)
+               return cms_keyid_cert_cmp(oik->d.subjectKeyIdentifier, cert);
+       return -1;
+       }
+
+int CMS_RecipientEncryptedKey_get0_id(CMS_RecipientEncryptedKey *rek,
+                                       ASN1_OCTET_STRING **keyid,
+                                       ASN1_GENERALIZEDTIME **tm,
+                                       CMS_OtherKeyAttribute **other,
+                                       X509_NAME **issuer, ASN1_INTEGER **sno)
+       {
+       CMS_KeyAgreeRecipientIdentifier *rid = rek->rid;
+       if (rid->type == CMS_REK_ISSUER_SERIAL)
+               {
+               if (issuer)
+                       *issuer = rid->d.issuerAndSerialNumber->issuer;
+               if (sno)
+                       *sno = rid->d.issuerAndSerialNumber->serialNumber;
+               if (keyid)
+                       *keyid = NULL;
+               if (tm)
+                       *tm = NULL;
+               if (other)
+                       *other = NULL;
+               }
+       else if (rid->type == CMS_REK_KEYIDENTIFIER)
+               {
+               if (keyid)
+                       *keyid = rid->d.rKeyId->subjectKeyIdentifier;
+               if (tm)
+                       *tm = rid->d.rKeyId->date;
+               if (other)
+                       *other = rid->d.rKeyId->other;
+               if (issuer)
+                       *issuer = NULL;
+               if (sno)
+                       *sno = NULL;
+               }
+       else
+               return 0;
+       return 1;
+       }
+
+int CMS_RecipientEncryptedKey_cert_cmp(CMS_RecipientEncryptedKey *rek,
+                                               X509 *cert)
+       {
+       CMS_KeyAgreeRecipientIdentifier *rid = rek->rid;
+       if (rid->type == CMS_REK_ISSUER_SERIAL)
+               return cms_ias_cert_cmp(rid->d.issuerAndSerialNumber, cert);
+       else if (rid->type == CMS_REK_KEYIDENTIFIER)
+               return cms_keyid_cert_cmp(rid->d.rKeyId->subjectKeyIdentifier, cert);
+       else
+               return -1;
+       }
+
+int CMS_RecipientInfo_kari_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk)
+       {
+       EVP_PKEY_CTX *pctx;
+       CMS_KeyAgreeRecipientInfo *kari = ri->d.kari;
+       if (kari->pctx)
+               {
+               EVP_PKEY_CTX_free(kari->pctx);
+               kari->pctx = NULL;
+               }
+       if (!pk)
+               return 1;
+       pctx = EVP_PKEY_CTX_new(pk, NULL);
+       if (!pctx || !EVP_PKEY_derive_init(pctx))
+               goto err;
+       kari->pctx = pctx;
+       return 1;
+       err:
+       if (pctx)
+               EVP_PKEY_CTX_free(pctx);
+       return 0;
+       }
+
+EVP_CIPHER_CTX *CMS_RecipientInfo_kari_get0_ctx(CMS_RecipientInfo *ri)
+       {
+       if (ri->type == CMS_RECIPINFO_AGREE)
+               return &ri->d.kari->ctx;
+       return NULL;
+       }
+
+/* Derive KEK and decrypt/encrypt with it to produce either the 
+ * original CEK or the encrypted CEK.
+ */
+
+static int cms_kek_cipher(unsigned char **pout, size_t *poutlen, 
+                       const unsigned char *in, size_t inlen,
+                       CMS_KeyAgreeRecipientInfo *kari, int enc)
+       {
+       /* Key encryption key */
+       unsigned char kek[EVP_MAX_KEY_LENGTH];
+       size_t keklen;
+       int rv = 0;
+       unsigned char *out = NULL;
+       int outlen;
+       keklen = EVP_CIPHER_CTX_key_length(&kari->ctx);
+       if (keklen > EVP_MAX_KEY_LENGTH)
+               return 0;
+       /* Derive KEK */
+       if (EVP_PKEY_derive(kari->pctx, kek, &keklen) <= 0)
+               goto err;
+       /* Set KEK in context */
+       if (!EVP_CipherInit_ex(&kari->ctx, NULL, NULL, kek, NULL, enc))
+               goto err;
+       /* obtain output length of ciphered key */
+       if (!EVP_CipherUpdate(&kari->ctx, NULL, &outlen, in, inlen))
+               goto err;
+       out = OPENSSL_malloc(outlen);
+       if (!out)
+               goto err;
+       if (!EVP_CipherUpdate(&kari->ctx, out, &outlen, in, inlen))
+               goto err;
+       *pout = out;
+       *poutlen = (size_t)outlen;
+       rv = 1;
+
+       err:
+       OPENSSL_cleanse(kek, keklen);
+       if (!rv && out)
+               OPENSSL_free(out);
+       EVP_CIPHER_CTX_cleanup(&kari->ctx);
+       EVP_PKEY_CTX_free(kari->pctx);
+       kari->pctx = NULL;
+       return rv;
+       }
+
+int CMS_RecipientInfo_kari_decrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
+                                               CMS_RecipientEncryptedKey *rek)
+       {
+       int rv = 0;
+       unsigned char *enckey = NULL, *cek = NULL;
+       size_t enckeylen;
+       size_t ceklen;
+       CMS_EncryptedContentInfo *ec;
+       enckeylen = rek->encryptedKey->length;
+       enckey = rek->encryptedKey->data;
+       /* Setup all parameters to derive KEK */
+       if (!cms_env_asn1_ctrl(ri, 1))
+               goto err;
+       /* Attempt to decrypt CEK */
+       if (!cms_kek_cipher(&cek, &ceklen, enckey, enckeylen, ri->d.kari, 0))
+               goto err;
+       ec = cms->d.envelopedData->encryptedContentInfo;
+       if (ec->key)
+               {
+               OPENSSL_cleanse(ec->key, ec->keylen);
+               OPENSSL_free(ec->key);
+               }
+       ec->key = cek;
+       ec->keylen = ceklen;
+       cek = NULL;
+       rv = 1;
+       err:
+       if (cek)
+               OPENSSL_free(cek);
+       return rv;
+       }
+
+/* Create ephemeral key and initialise context based on it */
+static int cms_kari_create_ephemeral_key(CMS_KeyAgreeRecipientInfo *kari,
+                                               EVP_PKEY *pk)
+       {
+       EVP_PKEY_CTX *pctx = NULL;
+       EVP_PKEY *ekey = NULL;
+       int rv = 0;
+       pctx = EVP_PKEY_CTX_new(pk, NULL);
+       if (!pctx)
+               goto err;
+       if (EVP_PKEY_keygen_init(pctx) <= 0)
+               goto err;
+       if (EVP_PKEY_keygen(pctx, &ekey) <= 0)
+               goto err;
+       EVP_PKEY_CTX_free(pctx);
+       pctx = EVP_PKEY_CTX_new(ekey, NULL);
+       if (!pctx)
+               goto err;
+       if (EVP_PKEY_derive_init(pctx) <= 0)
+               goto err;
+       kari->pctx = pctx;
+       rv = 1;
+       err:
+       if (!rv && pctx)
+               EVP_PKEY_CTX_free(pctx);
+       if (ekey)
+               EVP_PKEY_free(ekey);
+       return rv;
+       }
+
+/* Initialise a ktri based on passed certificate and key */
+
+int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip,
+                               EVP_PKEY *pk, unsigned int flags)
+       {
+       CMS_KeyAgreeRecipientInfo *kari;
+       CMS_RecipientEncryptedKey *rek = NULL;
+
+       ri->d.kari = M_ASN1_new_of(CMS_KeyAgreeRecipientInfo);
+       if (!ri->d.kari)
+               return 0;
+       ri->type = CMS_RECIPINFO_AGREE;
+
+       kari = ri->d.kari;
+       kari->version = 3;
+
+       rek = M_ASN1_new_of(CMS_RecipientEncryptedKey);
+       if (!sk_CMS_RecipientEncryptedKey_push(kari->recipientEncryptedKeys, rek))
+               {
+               M_ASN1_free_of(rek, CMS_RecipientEncryptedKey);
+               return 0;
+               }
+
+       if (flags & CMS_USE_KEYID)
+               {
+               rek->rid->type = CMS_REK_KEYIDENTIFIER;
+               if (!cms_set1_keyid(&rek->rid->d.rKeyId->subjectKeyIdentifier, recip))
+                       return 0;
+               }
+       else
+               {
+               rek->rid->type = CMS_REK_ISSUER_SERIAL;
+               if (!cms_set1_ias(&rek->rid->d.issuerAndSerialNumber, recip))
+                       return 0;
+               }
+
+       /* Create ephemeral key */
+       if (!cms_kari_create_ephemeral_key(kari, pk))
+               return 0;
+
+       CRYPTO_add(&pk->references, 1, CRYPTO_LOCK_EVP_PKEY);
+       rek->pkey = pk;
+       return 1;
+       }
+
+static int cms_wrap_init(CMS_KeyAgreeRecipientInfo *kari,
+                               const EVP_CIPHER *cipher)
+       {
+       EVP_CIPHER_CTX *ctx = &kari->ctx;
+       const EVP_CIPHER *kekcipher;
+       int keylen = EVP_CIPHER_key_length(cipher);
+       /* If a suitable wrap algorithm is already set nothing to do */
+       kekcipher = EVP_CIPHER_CTX_cipher(ctx);
+
+       if (kekcipher)
+               {
+               if (EVP_CIPHER_CTX_mode(ctx) != EVP_CIPH_WRAP_MODE)
+                       return 0;
+               return 1;
+               }
+       /* Pick a cipher based on content encryption cipher. If it is
+        * DES3 use DES3 wrap otherwise use AES wrap similar to key
+        * size.
+        */
+       if (EVP_CIPHER_type(cipher) == NID_des_ede3_cbc)
+               kekcipher = EVP_des_ede3_wrap();
+       else if (keylen <= 16)
+               kekcipher = EVP_aes_128_wrap();
+       else if (keylen <= 24)
+               kekcipher = EVP_aes_192_wrap();
+       else
+               kekcipher = EVP_aes_256_wrap();
+       return EVP_EncryptInit_ex(ctx, kekcipher, NULL, NULL, NULL);
+       }
+
+/* Encrypt content key in key agreement recipient info */
+
+int cms_RecipientInfo_kari_encrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri)
+       {
+       CMS_KeyAgreeRecipientInfo *kari;
+       CMS_EncryptedContentInfo *ec;
+       CMS_RecipientEncryptedKey *rek;
+       STACK_OF(CMS_RecipientEncryptedKey) *reks;
+       int i;
+
+       if (ri->type != CMS_RECIPINFO_AGREE)
+               {
+               CMSerr(CMS_F_CMS_RECIPIENTINFO_KARI_ENCRYPT,
+                       CMS_R_NOT_KEY_AGREEMENT);
+               return 0;
+               }
+       kari = ri->d.kari;
+       reks = kari->recipientEncryptedKeys;
+       ec = cms->d.envelopedData->encryptedContentInfo;
+       /* Initialise wrap algorithm parameters */
+       if (!cms_wrap_init(kari, ec->cipher))
+               return 0;
+       /* If no orignator key set up initialise for ephemeral key
+        * the public key ASN1 structure will set the actual public
+        * key value.
+        */
+       if (kari->originator->type == -1)
+               {
+               CMS_OriginatorIdentifierOrKey *oik = kari->originator;
+               oik->type = CMS_OIK_PUBKEY;
+               oik->d.originatorKey = M_ASN1_new_of(CMS_OriginatorPublicKey);
+               if (!oik->d.originatorKey)
+                       return 0;
+               }
+       /* Initialise KDF algorithm */
+       if (!cms_env_asn1_ctrl(ri, 0))
+               return 0;
+       /* For each rek, derive KEK, encrypt CEK */
+       for (i = 0; i < sk_CMS_RecipientEncryptedKey_num(reks); i++)
+               {
+               unsigned char *enckey;
+               size_t enckeylen;
+               rek = sk_CMS_RecipientEncryptedKey_value(reks, i);
+               if (EVP_PKEY_derive_set_peer(kari->pctx, rek->pkey) <= 0)
+                       return 0;
+               if (!cms_kek_cipher(&enckey, &enckeylen, ec->key, ec->keylen,
+                                                               kari, 1))
+                       return 0;
+               ASN1_STRING_set0(rek->encryptedKey, enckey, enckeylen);
+               }
+
+       return 1;
+
+       }
index b62dc72..dea7376 100644 (file)
@@ -83,10 +83,8 @@ typedef struct CMS_KeyTransRecipientInfo_st CMS_KeyTransRecipientInfo;
 typedef struct CMS_OriginatorPublicKey_st CMS_OriginatorPublicKey;
 typedef struct CMS_OriginatorIdentifierOrKey_st CMS_OriginatorIdentifierOrKey;
 typedef struct CMS_KeyAgreeRecipientInfo_st CMS_KeyAgreeRecipientInfo;
-typedef struct CMS_OtherKeyAttribute_st CMS_OtherKeyAttribute;
 typedef struct CMS_RecipientKeyIdentifier_st CMS_RecipientKeyIdentifier;
 typedef struct CMS_KeyAgreeRecipientIdentifier_st CMS_KeyAgreeRecipientIdentifier;
-typedef struct CMS_RecipientEncryptedKey_st CMS_RecipientEncryptedKey;
 typedef struct CMS_KEKIdentifier_st CMS_KEKIdentifier;
 typedef struct CMS_KEKRecipientInfo_st CMS_KEKRecipientInfo;
 typedef struct CMS_PasswordRecipientInfo_st CMS_PasswordRecipientInfo;
@@ -216,6 +214,10 @@ struct CMS_KeyAgreeRecipientInfo_st
        ASN1_OCTET_STRING *ukm;
        X509_ALGOR *keyEncryptionAlgorithm;
        STACK_OF(CMS_RecipientEncryptedKey) *recipientEncryptedKeys;
+       /* Public key context associated with current operation */
+       EVP_PKEY_CTX *pctx;
+       /* Cipher context for CEK wrapping */
+       EVP_CIPHER_CTX ctx;
        };
 
 struct CMS_OriginatorIdentifierOrKey_st
@@ -238,6 +240,8 @@ struct CMS_RecipientEncryptedKey_st
        {
        CMS_KeyAgreeRecipientIdentifier *rid;
        ASN1_OCTET_STRING *encryptedKey;
+       /* Public key associated with this recipient */
+       EVP_PKEY *pkey;
        };
 
 struct CMS_KeyAgreeRecipientIdentifier_st
@@ -431,6 +435,13 @@ DECLARE_ASN1_ALLOC_FUNCTIONS(CMS_IssuerAndSerialNumber)
 #define CMS_RECIPINFO_ISSUER_SERIAL    0
 #define CMS_RECIPINFO_KEYIDENTIFIER    1
 
+#define CMS_REK_ISSUER_SERIAL          0
+#define CMS_REK_KEYIDENTIFIER          1
+
+#define CMS_OIK_ISSUER_SERIAL          0
+#define        CMS_OIK_KEYIDENTIFIER           1
+#define        CMS_OIK_PUBKEY                  2
+
 BIO *cms_content_bio(CMS_ContentInfo *cms);
 
 CMS_ContentInfo *cms_Data_create(void);
@@ -454,6 +465,11 @@ BIO *cms_DigestAlgorithm_init_bio(X509_ALGOR *digestAlgorithm);
 int cms_DigestAlgorithm_find_ctx(EVP_MD_CTX *mctx, BIO *chain,
                                        X509_ALGOR *mdalg);
 
+int cms_ias_cert_cmp(CMS_IssuerAndSerialNumber *ias, X509 *cert);
+int cms_keyid_cert_cmp(ASN1_OCTET_STRING *keyid, X509 *cert);
+int cms_set1_ias(CMS_IssuerAndSerialNumber **pias, X509 *cert);
+int cms_set1_keyid(ASN1_OCTET_STRING **pkeyid, X509 *cert);
+
 BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec);
 BIO *cms_EncryptedData_init_bio(CMS_ContentInfo *cms);
 int cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec, 
@@ -466,6 +482,12 @@ ASN1_OCTET_STRING *cms_encode_Receipt(CMS_SignerInfo *si);
 
 BIO *cms_EnvelopedData_init_bio(CMS_ContentInfo *cms);
 CMS_EnvelopedData *cms_get0_enveloped(CMS_ContentInfo *cms);
+int cms_env_asn1_ctrl(CMS_RecipientInfo *ri, int cmd);
+int cms_pkey_get_ri_type(EVP_PKEY *pk);
+/* KARI routines */
+int cms_RecipientInfo_kari_init(CMS_RecipientInfo *ri, X509 *recip,
+                               EVP_PKEY *pk, unsigned int flags);
+int cms_RecipientInfo_kari_encrypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri);
 
 /* PWRI routines */
 int cms_RecipientInfo_pwri_crypt(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
index a5a186c..2682780 100644 (file)
@@ -52,7 +52,7 @@
  */
 
 #include <openssl/asn1t.h>
-#include <openssl/x509.h>
+#include <openssl/x509v3.h>
 #include <openssl/err.h>
 #include <openssl/pem.h>
 #include <openssl/bio.h>
@@ -607,3 +607,62 @@ STACK_OF(X509_CRL) *CMS_get1_crls(CMS_ContentInfo *cms)
                }
        return crls;
        }
+
+int cms_ias_cert_cmp(CMS_IssuerAndSerialNumber *ias, X509 *cert)
+       {
+       int ret;
+       ret = X509_NAME_cmp(ias->issuer, X509_get_issuer_name(cert));
+       if (ret)
+               return ret;
+       return ASN1_INTEGER_cmp(ias->serialNumber, X509_get_serialNumber(cert));
+       }
+
+int cms_keyid_cert_cmp(ASN1_OCTET_STRING *keyid, X509 *cert)
+       {
+       X509_check_purpose(cert, -1, -1);
+       if (!cert->skid)
+               return -1;
+       return ASN1_OCTET_STRING_cmp(keyid, cert->skid);
+       }
+
+int cms_set1_ias(CMS_IssuerAndSerialNumber **pias, X509 *cert)
+       {
+       CMS_IssuerAndSerialNumber *ias;
+       ias = M_ASN1_new_of(CMS_IssuerAndSerialNumber);
+       if (!ias)
+               goto err;
+       if (!X509_NAME_set(&ias->issuer, X509_get_issuer_name(cert)))
+               goto err;
+       if (!ASN1_STRING_copy(ias->serialNumber, X509_get_serialNumber(cert)))
+               goto err;
+       if (*pias)
+               M_ASN1_free_of(*pias, CMS_IssuerAndSerialNumber);
+       *pias = ias;
+       return 1;
+       err:
+       if (ias)
+               M_ASN1_free_of(ias, CMS_IssuerAndSerialNumber);
+       CMSerr(CMS_F_CMS_SET1_IAS, ERR_R_MALLOC_FAILURE);
+       return 0;
+       }
+
+int cms_set1_keyid(ASN1_OCTET_STRING **pkeyid, X509 *cert)
+       {
+       ASN1_OCTET_STRING *keyid = NULL;
+       X509_check_purpose(cert, -1, -1);
+       if (!cert->skid)
+               {
+               CMSerr(CMS_F_CMS_SET1_KEYID, CMS_R_CERTIFICATE_HAS_NO_KEYID);
+               return 0;
+               }
+       keyid = ASN1_STRING_dup(cert->skid);
+       if (!keyid)
+               {
+               CMSerr(CMS_F_CMS_SET1_KEYID, ERR_R_MALLOC_FAILURE);
+               return 0;
+               }
+       if (*pkeyid)
+               ASN1_OCTET_STRING_free(*pkeyid);
+       *pkeyid = keyid;
+       return 1;
+       }
index 8f67289..06a4a25 100644 (file)
@@ -54,6 +54,7 @@
 #include "cryptlib.h"
 #include <openssl/asn1t.h>
 #include <openssl/pem.h>
+#include <openssl/x509.h>
 #include <openssl/x509v3.h>
 #include <openssl/err.h>
 #include <openssl/cms.h>
@@ -212,29 +213,13 @@ int cms_set1_SignerIdentifier(CMS_SignerIdentifier *sid, X509 *cert, int type)
        switch(type)
                {
                case CMS_SIGNERINFO_ISSUER_SERIAL:
-               sid->d.issuerAndSerialNumber =
-                       M_ASN1_new_of(CMS_IssuerAndSerialNumber);
-               if (!sid->d.issuerAndSerialNumber)
-                       goto merr;
-               if (!X509_NAME_set(&sid->d.issuerAndSerialNumber->issuer,
-                                       X509_get_issuer_name(cert)))
-                       goto merr;
-               if (!ASN1_STRING_copy(
-                       sid->d.issuerAndSerialNumber->serialNumber,
-                               X509_get_serialNumber(cert)))
-                       goto merr;
+               if (!cms_set1_ias(&sid->d.issuerAndSerialNumber, cert))
+                       return 0;
                break;
 
                case CMS_SIGNERINFO_KEYIDENTIFIER:
-               if (!cert->skid)
-                       {
-                       CMSerr(CMS_F_CMS_SET1_SIGNERIDENTIFIER,
-                                       CMS_R_CERTIFICATE_HAS_NO_KEYID);
+               if (!cms_set1_keyid(&sid->d.subjectKeyIdentifier, cert))
                        return 0;
-                       }
-               sid->d.subjectKeyIdentifier = ASN1_STRING_dup(cert->skid);
-               if (!sid->d.subjectKeyIdentifier)
-                       goto merr;
                break;
 
                default:
@@ -245,11 +230,6 @@ int cms_set1_SignerIdentifier(CMS_SignerIdentifier *sid, X509 *cert, int type)
        sid->type = type;
 
        return 1;
-
-       merr:
-       CMSerr(CMS_F_CMS_SET1_SIGNERIDENTIFIER, ERR_R_MALLOC_FAILURE);
-       return 0;
-
        }
 
 int cms_SignerIdentifier_get0_signer_id(CMS_SignerIdentifier *sid,
@@ -275,24 +255,10 @@ int cms_SignerIdentifier_get0_signer_id(CMS_SignerIdentifier *sid,
 
 int cms_SignerIdentifier_cert_cmp(CMS_SignerIdentifier *sid, X509 *cert)
        {
-       int ret;
        if (sid->type == CMS_SIGNERINFO_ISSUER_SERIAL)
-               {
-               ret = X509_NAME_cmp(sid->d.issuerAndSerialNumber->issuer,
-                                       X509_get_issuer_name(cert));
-               if (ret)
-                       return ret;
-               return ASN1_INTEGER_cmp(sid->d.issuerAndSerialNumber->serialNumber,
-                                       X509_get_serialNumber(cert));
-               }
+               return cms_ias_cert_cmp(sid->d.issuerAndSerialNumber, cert);
        else if (sid->type == CMS_SIGNERINFO_KEYIDENTIFIER)
-               {
-               X509_check_purpose(cert, -1, -1);
-               if (!cert->skid)
-                       return -1;
-               return ASN1_OCTET_STRING_cmp(sid->d.subjectKeyIdentifier,
-                                                       cert->skid);
-               }
+               return cms_keyid_cert_cmp(sid->d.subjectKeyIdentifier, cert);
        else
                return -1;
        }
index 8c56e3a..204595b 100644 (file)
@@ -58,6 +58,7 @@
 #include <openssl/err.h>
 #include <openssl/cms.h>
 #include "cms_lcl.h"
+#include "asn1_locl.h"
 
 static int cms_copy_content(BIO *out, BIO *in, unsigned int flags)
        {
@@ -606,24 +607,65 @@ CMS_ContentInfo *CMS_encrypt(STACK_OF(X509) *certs, BIO *data,
        return NULL;
        }
 
+static int cms_kari_set1_pkey(CMS_ContentInfo *cms, CMS_RecipientInfo *ri,
+                                               EVP_PKEY *pk, X509 *cert)
+       {
+       int i;
+       STACK_OF(CMS_RecipientEncryptedKey) *reks;
+       CMS_RecipientEncryptedKey *rek;
+       reks = CMS_RecipientInfo_kari_get0_reks(ri);
+       if (!cert)
+               return 0;
+       for (i = 0; i < sk_CMS_RecipientEncryptedKey_num(reks); i++)
+               {
+               int rv;
+               rek = sk_CMS_RecipientEncryptedKey_value(reks, i);
+               if (CMS_RecipientEncryptedKey_cert_cmp(rek, cert))
+                       continue;
+               CMS_RecipientInfo_kari_set0_pkey(ri, pk);
+               rv = CMS_RecipientInfo_kari_decrypt(cms, ri, rek);
+               CMS_RecipientInfo_kari_set0_pkey(ri, NULL);
+               if (rv > 0)
+                       return 1;
+               return -1;
+               }
+       return 0;
+       }
+
 int CMS_decrypt_set1_pkey(CMS_ContentInfo *cms, EVP_PKEY *pk, X509 *cert)
        {
        STACK_OF(CMS_RecipientInfo) *ris;
        CMS_RecipientInfo *ri;
-       int i, r;
+       int i, r, ri_type;
        int debug = 0;
        ris = CMS_get0_RecipientInfos(cms);
        if (ris)
                debug = cms->d.envelopedData->encryptedContentInfo->debug;
+       ri_type = cms_pkey_get_ri_type(pk);
+       if (ri_type == CMS_RECIPINFO_NONE)
+               {
+               CMSerr(CMS_F_CMS_DECRYPT_SET1_PKEY,
+                                       CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
+               return 0;
+               }
+
        for (i = 0; i < sk_CMS_RecipientInfo_num(ris); i++)
                {
                ri = sk_CMS_RecipientInfo_value(ris, i);
-               if (CMS_RecipientInfo_type(ri) != CMS_RECIPINFO_TRANS)
+               if (CMS_RecipientInfo_type(ri) != ri_type)
                                continue;
+               if (ri_type == CMS_RECIPINFO_AGREE)
+                       {
+                       r = cms_kari_set1_pkey(cms, ri, pk, cert);
+                       if (r > 0)
+                               return 1;
+                       if (r < 0)
+                               return 0;
+                       }
                /* If we have a cert try matching RecipientInfo
                 * otherwise try them all.
                 */
-               if (!cert || (CMS_RecipientInfo_ktri_cert_cmp(ri, cert) == 0))
+               else if (!cert || !CMS_RecipientInfo_ktri_cert_cmp(ri, cert))
                        {
                        CMS_RecipientInfo_set0_pkey(ri, pk);
                        r = CMS_RecipientInfo_decrypt(cms, ri);
index b99f6da..69667df 100644 (file)
@@ -1048,6 +1048,7 @@ void EVP_PBE_cleanup(void);
 #define ASN1_PKEY_CTRL_DEFAULT_MD_NID  0x3
 #define ASN1_PKEY_CTRL_CMS_SIGN                0x5
 #define ASN1_PKEY_CTRL_CMS_ENVELOPE    0x7
+#define ASN1_PKEY_CTRL_CMS_RI_TYPE     0x8
 
 int EVP_PKEY_asn1_get_count(void);
 const EVP_PKEY_ASN1_METHOD *EVP_PKEY_asn1_get0(int idx);
index e9c99b8..293e6fd 100644 (file)
@@ -491,6 +491,28 @@ DECLARE_SPECIAL_STACK_OF(OPENSSL_BLOCK, void)
 #define sk_CMS_CertificateChoices_sort(st) SKM_sk_sort(CMS_CertificateChoices, (st))
 #define sk_CMS_CertificateChoices_is_sorted(st) SKM_sk_is_sorted(CMS_CertificateChoices, (st))
 
+#define sk_CMS_RecipientEncryptedKey_new(cmp) SKM_sk_new(CMS_RecipientEncryptedKey, (cmp))
+#define sk_CMS_RecipientEncryptedKey_new_null() SKM_sk_new_null(CMS_RecipientEncryptedKey)
+#define sk_CMS_RecipientEncryptedKey_free(st) SKM_sk_free(CMS_RecipientEncryptedKey, (st))
+#define sk_CMS_RecipientEncryptedKey_num(st) SKM_sk_num(CMS_RecipientEncryptedKey, (st))
+#define sk_CMS_RecipientEncryptedKey_value(st, i) SKM_sk_value(CMS_RecipientEncryptedKey, (st), (i))
+#define sk_CMS_RecipientEncryptedKey_set(st, i, val) SKM_sk_set(CMS_RecipientEncryptedKey, (st), (i), (val))
+#define sk_CMS_RecipientEncryptedKey_zero(st) SKM_sk_zero(CMS_RecipientEncryptedKey, (st))
+#define sk_CMS_RecipientEncryptedKey_push(st, val) SKM_sk_push(CMS_RecipientEncryptedKey, (st), (val))
+#define sk_CMS_RecipientEncryptedKey_unshift(st, val) SKM_sk_unshift(CMS_RecipientEncryptedKey, (st), (val))
+#define sk_CMS_RecipientEncryptedKey_find(st, val) SKM_sk_find(CMS_RecipientEncryptedKey, (st), (val))
+#define sk_CMS_RecipientEncryptedKey_find_ex(st, val) SKM_sk_find_ex(CMS_RecipientEncryptedKey, (st), (val))
+#define sk_CMS_RecipientEncryptedKey_delete(st, i) SKM_sk_delete(CMS_RecipientEncryptedKey, (st), (i))
+#define sk_CMS_RecipientEncryptedKey_delete_ptr(st, ptr) SKM_sk_delete_ptr(CMS_RecipientEncryptedKey, (st), (ptr))
+#define sk_CMS_RecipientEncryptedKey_insert(st, val, i) SKM_sk_insert(CMS_RecipientEncryptedKey, (st), (val), (i))
+#define sk_CMS_RecipientEncryptedKey_set_cmp_func(st, cmp) SKM_sk_set_cmp_func(CMS_RecipientEncryptedKey, (st), (cmp))
+#define sk_CMS_RecipientEncryptedKey_dup(st) SKM_sk_dup(CMS_RecipientEncryptedKey, st)
+#define sk_CMS_RecipientEncryptedKey_pop_free(st, free_func) SKM_sk_pop_free(CMS_RecipientEncryptedKey, (st), (free_func))
+#define sk_CMS_RecipientEncryptedKey_shift(st) SKM_sk_shift(CMS_RecipientEncryptedKey, (st))
+#define sk_CMS_RecipientEncryptedKey_pop(st) SKM_sk_pop(CMS_RecipientEncryptedKey, (st))
+#define sk_CMS_RecipientEncryptedKey_sort(st) SKM_sk_sort(CMS_RecipientEncryptedKey, (st))
+#define sk_CMS_RecipientEncryptedKey_is_sorted(st) SKM_sk_is_sorted(CMS_RecipientEncryptedKey, (st))
+
 #define sk_CMS_RecipientInfo_new(cmp) SKM_sk_new(CMS_RecipientInfo, (cmp))
 #define sk_CMS_RecipientInfo_new_null() SKM_sk_new_null(CMS_RecipientInfo)
 #define sk_CMS_RecipientInfo_free(st) SKM_sk_free(CMS_RecipientInfo, (st))