diff options
author | Dr. Stephen Henson <steve@openssl.org> | 2013-07-17 14:36:39 +0100 |
---|---|---|
committer | Dr. Stephen Henson <steve@openssl.org> | 2013-10-01 14:01:18 +0100 |
commit | 8c798690ce6092ba33eb4f7505c3c036cce20e8c (patch) | |
tree | c9a06d6af1da2e5a896d0f08478b36591ce26b15 /crypto/cms | |
parent | ea6bf266577e3a0bbc85c241686508f88b10881f (diff) |
CMS support for key agreeement recipient info.
Add hooks to support key agreement recipient info type (KARI) using
algorithm specific code in the relevant public key ASN1 method.
(cherry picked from commit 17c2764d2e6fc5a010ad3c12662068689ed2ad17)
Diffstat (limited to 'crypto/cms')
-rw-r--r-- | crypto/cms/Makefile | 4 | ||||
-rw-r--r-- | crypto/cms/cms.h | 38 | ||||
-rw-r--r-- | crypto/cms/cms_asn1.c | 40 | ||||
-rw-r--r-- | crypto/cms/cms_env.c | 141 | ||||
-rw-r--r-- | crypto/cms/cms_err.c | 10 | ||||
-rw-r--r-- | crypto/cms/cms_kari.c | 480 | ||||
-rw-r--r-- | crypto/cms/cms_lcl.h | 26 | ||||
-rw-r--r-- | crypto/cms/cms_lib.c | 61 | ||||
-rw-r--r-- | crypto/cms/cms_sd.c | 46 | ||||
-rw-r--r-- | crypto/cms/cms_smime.c | 48 |
10 files changed, 801 insertions, 93 deletions
diff --git a/crypto/cms/Makefile b/crypto/cms/Makefile index 9820adb212..f17ae347a9 100644 --- a/crypto/cms/Makefile +++ b/crypto/cms/Makefile @@ -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) diff --git a/crypto/cms/cms.h b/crypto/cms/cms.h index 1624b79ebc..f644cbfbc4 100644 --- a/crypto/cms/cms.h +++ b/crypto/cms/cms.h @@ -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 diff --git a/crypto/cms/cms_asn1.c b/crypto/cms/cms_asn1.c index 6a692cdb1d..7d79db221a 100644 --- a/crypto/cms/cms_asn1.c +++ b/crypto/cms/cms_asn1.c @@ -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), diff --git a/crypto/cms/cms_env.c b/crypto/cms/cms_env.c index cef825be2b..8c6d91f3c8 100644 --- a/crypto/cms/cms_env.c +++ b/crypto/cms/cms_env.c @@ -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; + } diff --git a/crypto/cms/cms_err.c b/crypto/cms/cms_err.c index 35aca59335..5d9f685113 100644 --- a/crypto/cms/cms_err.c +++ b/crypto/cms/cms_err.c @@ -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 index 0000000000..2451f0f23f --- /dev/null +++ b/crypto/cms/cms_kari.c @@ -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; + + } diff --git a/crypto/cms/cms_lcl.h b/crypto/cms/cms_lcl.h index b107cd2d76..7385eaba75 100644 --- a/crypto/cms/cms_lcl.h +++ b/crypto/cms/cms_lcl.h @@ -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 C |