Add CMS AuthEnvelopedData with AES-GCM support
[openssl.git] / crypto / cms / cms_enc.c
1 /*
2  * Copyright 2008-2020 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 #include "internal/cryptlib.h"
11 #include <openssl/asn1t.h>
12 #include <openssl/pem.h>
13 #include <openssl/x509v3.h>
14 #include <openssl/err.h>
15 #include <openssl/cms.h>
16 #include <openssl/rand.h>
17 #include "crypto/evp.h"
18 #include "cms_local.h"
19
20 /* CMS EncryptedData Utilities */
21
22 /* Return BIO based on EncryptedContentInfo and key */
23
24 BIO *cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec,
25                                    const CMS_CTX *cms_ctx)
26 {
27     BIO *b;
28     EVP_CIPHER_CTX *ctx;
29     EVP_CIPHER *fetched_ciph = NULL;
30     const EVP_CIPHER *cipher = NULL;
31     X509_ALGOR *calg = ec->contentEncryptionAlgorithm;
32     evp_cipher_aead_asn1_params aparams;
33     unsigned char iv[EVP_MAX_IV_LENGTH], *piv = NULL;
34     unsigned char *tkey = NULL;
35     int len;
36     int ivlen = 0;
37     size_t tkeylen = 0;
38     int ok = 0;
39     int enc, keep_key = 0;
40
41     enc = ec->cipher ? 1 : 0;
42
43     b = BIO_new(BIO_f_cipher());
44     if (b == NULL) {
45         CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO, ERR_R_MALLOC_FAILURE);
46         return NULL;
47     }
48
49     BIO_get_cipher_ctx(b, &ctx);
50
51     (void)ERR_set_mark();
52     if (enc) {
53         cipher = ec->cipher;
54         /*
55          * If not keeping key set cipher to NULL so subsequent calls decrypt.
56          */
57         if (ec->key != NULL)
58             ec->cipher = NULL;
59     } else {
60         cipher = EVP_get_cipherbyobj(calg->algorithm);
61     }
62     if (cipher != NULL) {
63         fetched_ciph = EVP_CIPHER_fetch(cms_ctx->libctx, EVP_CIPHER_name(cipher),
64                                         cms_ctx->propq);
65         if (fetched_ciph != NULL)
66             cipher = fetched_ciph;
67     }
68     if (cipher == NULL) {
69         (void)ERR_clear_last_mark();
70         CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO, CMS_R_UNKNOWN_CIPHER);
71         goto err;
72     }
73     (void)ERR_pop_to_mark();
74
75     if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, enc) <= 0) {
76         CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
77                CMS_R_CIPHER_INITIALISATION_ERROR);
78         goto err;
79     }
80
81     if (enc) {
82         calg->algorithm = OBJ_nid2obj(EVP_CIPHER_CTX_type(ctx));
83         /* Generate a random IV if we need one */
84         ivlen = EVP_CIPHER_CTX_iv_length(ctx);
85         if (ivlen > 0) {
86             if (RAND_bytes_ex(cms_ctx->libctx, iv, ivlen) <= 0)
87                 goto err;
88             piv = iv;
89         }
90     } else {
91         if (evp_cipher_asn1_to_param_ex(ctx, calg->parameter, &aparams) <= 0) {
92             CMSerr(0, CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
93             goto err;
94         }
95         if ((EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)) {
96             piv = aparams.iv;
97             if (ec->taglen > 0
98                     && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
99                                            ec->taglen, ec->tag) <= 0) {
100                 CMSerr(0, CMS_R_CIPHER_AEAD_SET_TAG_ERROR);
101                 goto err;
102             }
103         }
104     }
105     len = EVP_CIPHER_CTX_key_length(ctx);
106     if (len <= 0)
107         goto err;
108     tkeylen = (size_t)len;
109
110     /* Generate random session key */
111     if (!enc || !ec->key) {
112         tkey = OPENSSL_malloc(tkeylen);
113         if (tkey == NULL) {
114             CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO, ERR_R_MALLOC_FAILURE);
115             goto err;
116         }
117         if (EVP_CIPHER_CTX_rand_key(ctx, tkey) <= 0)
118             goto err;
119     }
120
121     if (!ec->key) {
122         ec->key = tkey;
123         ec->keylen = tkeylen;
124         tkey = NULL;
125         if (enc)
126             keep_key = 1;
127         else
128             ERR_clear_error();
129
130     }
131
132     if (ec->keylen != tkeylen) {
133         /* If necessary set key length */
134         if (EVP_CIPHER_CTX_set_key_length(ctx, ec->keylen) <= 0) {
135             /*
136              * Only reveal failure if debugging so we don't leak information
137              * which may be useful in MMA.
138              */
139             if (enc || ec->debug) {
140                 CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
141                        CMS_R_INVALID_KEY_LENGTH);
142                 goto err;
143             } else {
144                 /* Use random key */
145                 OPENSSL_clear_free(ec->key, ec->keylen);
146                 ec->key = tkey;
147                 ec->keylen = tkeylen;
148                 tkey = NULL;
149                 ERR_clear_error();
150             }
151         }
152     }
153
154     if (EVP_CipherInit_ex(ctx, NULL, NULL, ec->key, piv, enc) <= 0) {
155         CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
156                CMS_R_CIPHER_INITIALISATION_ERROR);
157         goto err;
158     }
159     if (enc) {
160         calg->parameter = ASN1_TYPE_new();
161         if (calg->parameter == NULL) {
162             CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO, ERR_R_MALLOC_FAILURE);
163             goto err;
164         }
165         if ((EVP_CIPHER_flags(cipher) & EVP_CIPH_FLAG_AEAD_CIPHER)) {
166             memcpy(aparams.iv, piv, ivlen);
167             aparams.iv_len = ivlen;
168             aparams.tag_len = EVP_CIPHER_CTX_tag_length(ctx);
169             if (aparams.tag_len <= 0)
170                 goto err;
171         }
172
173         if (evp_cipher_param_to_asn1_ex(ctx, calg->parameter, &aparams) <= 0) {
174             CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT_BIO,
175                    CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
176             goto err;
177         }
178         /* If parameter type not set omit parameter */
179         if (calg->parameter->type == V_ASN1_UNDEF) {
180             ASN1_TYPE_free(calg->parameter);
181             calg->parameter = NULL;
182         }
183     }
184     ok = 1;
185
186  err:
187     EVP_CIPHER_free(fetched_ciph);
188     if (!keep_key || !ok) {
189         OPENSSL_clear_free(ec->key, ec->keylen);
190         ec->key = NULL;
191     }
192     OPENSSL_clear_free(tkey, tkeylen);
193     if (ok)
194         return b;
195     BIO_free(b);
196     return NULL;
197 }
198
199 int cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec,
200                               const EVP_CIPHER *cipher,
201                               const unsigned char *key, size_t keylen,
202                               const CMS_CTX *cms_ctx)
203 {
204     ec->cipher = cipher;
205     if (key) {
206         if ((ec->key = OPENSSL_malloc(keylen)) == NULL) {
207             CMSerr(CMS_F_CMS_ENCRYPTEDCONTENT_INIT, ERR_R_MALLOC_FAILURE);
208             return 0;
209         }
210         memcpy(ec->key, key, keylen);
211     }
212     ec->keylen = keylen;
213     if (cipher != NULL)
214         ec->contentType = OBJ_nid2obj(NID_pkcs7_data);
215     return 1;
216 }
217
218 int CMS_EncryptedData_set1_key(CMS_ContentInfo *cms, const EVP_CIPHER *ciph,
219                                const unsigned char *key, size_t keylen)
220 {
221     CMS_EncryptedContentInfo *ec;
222
223     if (!key || !keylen) {
224         CMSerr(CMS_F_CMS_ENCRYPTEDDATA_SET1_KEY, CMS_R_NO_KEY);
225         return 0;
226     }
227     if (ciph) {
228         cms->d.encryptedData = M_ASN1_new_of(CMS_EncryptedData);
229         if (!cms->d.encryptedData) {
230             CMSerr(CMS_F_CMS_ENCRYPTEDDATA_SET1_KEY, ERR_R_MALLOC_FAILURE);
231             return 0;
232         }
233         cms->contentType = OBJ_nid2obj(NID_pkcs7_encrypted);
234         cms->d.encryptedData->version = 0;
235     } else if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_encrypted) {
236         CMSerr(CMS_F_CMS_ENCRYPTEDDATA_SET1_KEY, CMS_R_NOT_ENCRYPTED_DATA);
237         return 0;
238     }
239     ec = cms->d.encryptedData->encryptedContentInfo;
240     return cms_EncryptedContent_init(ec, ciph, key, keylen, cms_get0_cmsctx(cms));
241 }
242
243 BIO *cms_EncryptedData_init_bio(const CMS_ContentInfo *cms)
244 {
245     CMS_EncryptedData *enc = cms->d.encryptedData;
246     if (enc->encryptedContentInfo->cipher && enc->unprotectedAttrs)
247         enc->version = 2;
248     return cms_EncryptedContent_init_bio(enc->encryptedContentInfo,
249                                          cms_get0_cmsctx(cms));
250 }