1 /* $OpenBSD: cms_enc.c,v 1.20 2019/08/11 11:04:18 jsing Exp $ */
2 /*
3 * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
4 * project.
5 */
6 /* ====================================================================
7 * Copyright (c) 2008 The OpenSSL Project. All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 *
21 * 3. All advertising materials mentioning features or use of this
22 * software must display the following acknowledgment:
23 * "This product includes software developed by the OpenSSL Project
24 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25 *
26 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27 * endorse or promote products derived from this software without
28 * prior written permission. For written permission, please contact
29 * licensing@OpenSSL.org.
30 *
31 * 5. Products derived from this software may not be called "OpenSSL"
32 * nor may "OpenSSL" appear in their names without prior written
33 * permission of the OpenSSL Project.
34 *
35 * 6. Redistributions of any form whatsoever must retain the following
36 * acknowledgment:
37 * "This product includes software developed by the OpenSSL Project
38 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51 * OF THE POSSIBILITY OF SUCH DAMAGE.
52 * ====================================================================
53 */
54
55 #include <string.h>
56
57 #include "cryptlib.h"
58 #include <openssl/asn1t.h>
59 #include <openssl/pem.h>
60 #include <openssl/x509v3.h>
61 #include <openssl/err.h>
62 #include <openssl/cms.h>
63 #include <openssl/rand.h>
64 #include "cms_lcl.h"
65
66 /* CMS EncryptedData Utilities */
67
68 /* Return BIO based on EncryptedContentInfo and key */
69
70 BIO *
cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo * ec)71 cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
72 {
73 BIO *b;
74 EVP_CIPHER_CTX *ctx;
75 const EVP_CIPHER *ciph;
76 X509_ALGOR *calg = ec->contentEncryptionAlgorithm;
77 unsigned char iv[EVP_MAX_IV_LENGTH], *piv = NULL;
78 unsigned char *tkey = NULL;
79 size_t tkeylen = 0;
80
81 int ok = 0;
82
83 int enc, keep_key = 0;
84
85 enc = ec->cipher ? 1 : 0;
86
87 b = BIO_new(BIO_f_cipher());
88 if (b == NULL) {
89 CMSerror(ERR_R_MALLOC_FAILURE);
90 return NULL;
91 }
92
93 BIO_get_cipher_ctx(b, &ctx);
94
95 if (enc) {
96 ciph = ec->cipher;
97 /*
98 * If not keeping key set cipher to NULL so subsequent calls decrypt.
99 */
100 if (ec->key)
101 ec->cipher = NULL;
102 } else {
103 ciph = EVP_get_cipherbyobj(calg->algorithm);
104
105 if (!ciph) {
106 CMSerror(CMS_R_UNKNOWN_CIPHER);
107 goto err;
108 }
109 }
110
111 if (EVP_CipherInit_ex(ctx, ciph, NULL, NULL, NULL, enc) <= 0) {
112 CMSerror(CMS_R_CIPHER_INITIALISATION_ERROR);
113 goto err;
114 }
115
116 if (enc) {
117 int ivlen;
118 calg->algorithm = OBJ_nid2obj(EVP_CIPHER_CTX_type(ctx));
119 /* Generate a random IV if we need one */
120 ivlen = EVP_CIPHER_CTX_iv_length(ctx);
121 if (ivlen > 0) {
122 arc4random_buf(iv, ivlen);
123 piv = iv;
124 }
125 } else if (EVP_CIPHER_asn1_to_param(ctx, calg->parameter) <= 0) {
126 CMSerror(CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
127 goto err;
128 }
129 tkeylen = EVP_CIPHER_CTX_key_length(ctx);
130 /* Generate random session key */
131 if (!enc || !ec->key) {
132 tkey = malloc(tkeylen);
133 if (tkey == NULL) {
134 CMSerror(ERR_R_MALLOC_FAILURE);
135 goto err;
136 }
137 if (EVP_CIPHER_CTX_rand_key(ctx, tkey) <= 0)
138 goto err;
139 }
140
141 if (!ec->key) {
142 ec->key = tkey;
143 ec->keylen = tkeylen;
144 tkey = NULL;
145 if (enc)
146 keep_key = 1;
147 else
148 ERR_clear_error();
149
150 }
151
152 if (ec->keylen != tkeylen) {
153 /* If necessary set key length */
154 if (EVP_CIPHER_CTX_set_key_length(ctx, ec->keylen) <= 0) {
155 /*
156 * Only reveal failure if debugging so we don't leak information
157 * which may be useful in MMA.
158 */
159 if (enc || ec->debug) {
160 CMSerror(CMS_R_INVALID_KEY_LENGTH);
161 goto err;
162 } else {
163 /* Use random key */
164 freezero(ec->key, ec->keylen);
165 ec->key = tkey;
166 ec->keylen = tkeylen;
167 tkey = NULL;
168 ERR_clear_error();
169 }
170 }
171 }
172
173 if (EVP_CipherInit_ex(ctx, NULL, NULL, ec->key, piv, enc) <= 0) {
174 CMSerror(CMS_R_CIPHER_INITIALISATION_ERROR);
175 goto err;
176 }
177 if (enc) {
178 calg->parameter = ASN1_TYPE_new();
179 if (calg->parameter == NULL) {
180 CMSerror(ERR_R_MALLOC_FAILURE);
181 goto err;
182 }
183 if (EVP_CIPHER_param_to_asn1(ctx, calg->parameter) <= 0) {
184 CMSerror(CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
185 goto err;
186 }
187 /* If parameter type not set omit parameter */
188 if (calg->parameter->type == V_ASN1_UNDEF) {
189 ASN1_TYPE_free(calg->parameter);
190 calg->parameter = NULL;
191 }
192 }
193 ok = 1;
194
195 err:
196 if (!keep_key || !ok) {
197 freezero(ec->key, ec->keylen);
198 ec->key = NULL;
199 }
200 freezero(tkey, tkeylen);
201 if (ok)
202 return b;
203 BIO_free(b);
204 return NULL;
205 }
206
207 int
cms_EncryptedContent_init(CMS_EncryptedContentInfo * ec,const EVP_CIPHER * cipher,const unsigned char * key,size_t keylen)208 cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec,
209 const EVP_CIPHER *cipher, const unsigned char *key, size_t keylen)
210 {
211 ec->cipher = cipher;
212 if (key) {
213 if ((ec->key = malloc(keylen)) == NULL) {
214 CMSerror(ERR_R_MALLOC_FAILURE);
215 return 0;
216 }
217 memcpy(ec->key, key, keylen);
218 }
219 ec->keylen = keylen;
220 if (cipher)
221 ec->contentType = OBJ_nid2obj(NID_pkcs7_data);
222
223 return 1;
224 }
225
226 int
CMS_EncryptedData_set1_key(CMS_ContentInfo * cms,const EVP_CIPHER * ciph,const unsigned char * key,size_t keylen)227 CMS_EncryptedData_set1_key(CMS_ContentInfo *cms, const EVP_CIPHER *ciph,
228 const unsigned char *key, size_t keylen)
229 {
230 CMS_EncryptedContentInfo *ec;
231
232 if (!key || !keylen) {
233 CMSerror(CMS_R_NO_KEY);
234 return 0;
235 }
236 if (ciph) {
237 cms->d.encryptedData = (CMS_EncryptedData *)ASN1_item_new(&CMS_EncryptedData_it);
238 if (!cms->d.encryptedData) {
239 CMSerror(ERR_R_MALLOC_FAILURE);
240 return 0;
241 }
242 cms->contentType = OBJ_nid2obj(NID_pkcs7_encrypted);
243 cms->d.encryptedData->version = 0;
244 } else if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_encrypted) {
245 CMSerror(CMS_R_NOT_ENCRYPTED_DATA);
246 return 0;
247 }
248 ec = cms->d.encryptedData->encryptedContentInfo;
249
250 return cms_EncryptedContent_init(ec, ciph, key, keylen);
251 }
252
253 BIO *
cms_EncryptedData_init_bio(CMS_ContentInfo * cms)254 cms_EncryptedData_init_bio(CMS_ContentInfo *cms)
255 {
256 CMS_EncryptedData *enc = cms->d.encryptedData;
257
258 if (enc->encryptedContentInfo->cipher && enc->unprotectedAttrs)
259 enc->version = 2;
260
261 return cms_EncryptedContent_init_bio(enc->encryptedContentInfo);
262 }
263