xref: /openbsd-src/lib/libcrypto/cms/cms_enc.c (revision b8a66e7e2197f63dc3ffee7681fe19ca0a104b5b)
1 /* $OpenBSD: cms_enc.c,v 1.25 2024/11/01 18:34:06 tb 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 <stdlib.h>
56 #include <string.h>
57 
58 #include <openssl/asn1.h>
59 #include <openssl/bio.h>
60 #include <openssl/cms.h>
61 #include <openssl/err.h>
62 #include <openssl/evp.h>
63 #include <openssl/objects.h>
64 #include <openssl/x509.h>
65 
66 #include "cms_local.h"
67 #include "evp_local.h"
68 
69 /* CMS EncryptedData Utilities */
70 
71 /* Return BIO based on EncryptedContentInfo and key */
72 
73 BIO *
74 cms_EncryptedContent_init_bio(CMS_EncryptedContentInfo *ec)
75 {
76 	BIO *b;
77 	EVP_CIPHER_CTX *ctx;
78 	const EVP_CIPHER *ciph;
79 	X509_ALGOR *calg = ec->contentEncryptionAlgorithm;
80 	unsigned char iv[EVP_MAX_IV_LENGTH], *piv = NULL;
81 	unsigned char *tkey = NULL;
82 	size_t tkeylen = 0;
83 
84 	int ok = 0;
85 
86 	int enc, keep_key = 0;
87 
88 	enc = ec->cipher ? 1 : 0;
89 
90 	b = BIO_new(BIO_f_cipher());
91 	if (b == NULL) {
92 		CMSerror(ERR_R_MALLOC_FAILURE);
93 		return NULL;
94 	}
95 
96 	BIO_get_cipher_ctx(b, &ctx);
97 
98 	if (enc) {
99 		ciph = ec->cipher;
100 		/*
101 		 * If not keeping key set cipher to NULL so subsequent calls decrypt.
102 		 */
103 		if (ec->key)
104 			ec->cipher = NULL;
105 	} else {
106 		ciph = EVP_get_cipherbyobj(calg->algorithm);
107 
108 		if (!ciph) {
109 			CMSerror(CMS_R_UNKNOWN_CIPHER);
110 			goto err;
111 		}
112 	}
113 
114 	if (EVP_CipherInit_ex(ctx, ciph, NULL, NULL, NULL, enc) <= 0) {
115 		CMSerror(CMS_R_CIPHER_INITIALISATION_ERROR);
116 		goto err;
117 	}
118 
119 	if (enc) {
120 		int ivlen;
121 		calg->algorithm = OBJ_nid2obj(EVP_CIPHER_CTX_type(ctx));
122 		/* Generate a random IV if we need one */
123 		ivlen = EVP_CIPHER_CTX_iv_length(ctx);
124 		if (ivlen > 0) {
125 			arc4random_buf(iv, ivlen);
126 			piv = iv;
127 		}
128 	} else if (EVP_CIPHER_asn1_to_param(ctx, calg->parameter) <= 0) {
129 		CMSerror(CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
130 		goto err;
131 	}
132 	tkeylen = EVP_CIPHER_CTX_key_length(ctx);
133 	/* Generate random session key */
134 	if (!enc || !ec->key) {
135 		tkey = malloc(tkeylen);
136 		if (tkey == NULL) {
137 			CMSerror(ERR_R_MALLOC_FAILURE);
138 			goto err;
139 		}
140 		if (EVP_CIPHER_CTX_rand_key(ctx, tkey) <= 0)
141 			goto err;
142 	}
143 
144 	if (!ec->key) {
145 		ec->key = tkey;
146 		ec->keylen = tkeylen;
147 		tkey = NULL;
148 		if (enc)
149 			keep_key = 1;
150 		else
151 			ERR_clear_error();
152 
153 	}
154 
155 	if (ec->keylen != tkeylen) {
156 		/* If necessary set key length */
157 		if (!EVP_CIPHER_CTX_set_key_length(ctx, ec->keylen)) {
158 			/*
159 			 * Only reveal failure if debugging so we don't leak information
160 			 * which may be useful in MMA.
161 			 */
162 			if (enc || ec->debug) {
163 			    CMSerror(CMS_R_INVALID_KEY_LENGTH);
164 			    goto err;
165 			} else {
166 			    /* Use random key */
167 			    freezero(ec->key, ec->keylen);
168 			    ec->key = tkey;
169 			    ec->keylen = tkeylen;
170 			    tkey = NULL;
171 			    ERR_clear_error();
172 			}
173 		}
174 	}
175 
176 	if (EVP_CipherInit_ex(ctx, NULL, NULL, ec->key, piv, enc) <= 0) {
177 		CMSerror(CMS_R_CIPHER_INITIALISATION_ERROR);
178 		goto err;
179 	}
180 	if (enc) {
181 		calg->parameter = ASN1_TYPE_new();
182 		if (calg->parameter == NULL) {
183 			CMSerror(ERR_R_MALLOC_FAILURE);
184 			goto err;
185 		}
186 		if (EVP_CIPHER_param_to_asn1(ctx, calg->parameter) <= 0) {
187 			CMSerror(CMS_R_CIPHER_PARAMETER_INITIALISATION_ERROR);
188 			goto err;
189 		}
190 		/* If parameter type not set omit parameter */
191 		if (calg->parameter->type == V_ASN1_UNDEF) {
192 			ASN1_TYPE_free(calg->parameter);
193 			calg->parameter = NULL;
194 		}
195 	}
196 	ok = 1;
197 
198  err:
199 	if (!keep_key || !ok) {
200 		freezero(ec->key, ec->keylen);
201 		ec->key = NULL;
202 	}
203 	freezero(tkey, tkeylen);
204 	if (ok)
205 		return b;
206 	BIO_free(b);
207 	return NULL;
208 }
209 
210 int
211 cms_EncryptedContent_init(CMS_EncryptedContentInfo *ec,
212     const EVP_CIPHER *cipher, const unsigned char *key, size_t keylen)
213 {
214 	ec->cipher = cipher;
215 	if (key) {
216 		if ((ec->key = malloc(keylen)) == NULL) {
217 			CMSerror(ERR_R_MALLOC_FAILURE);
218 			return 0;
219 		}
220 		memcpy(ec->key, key, keylen);
221 	}
222 	ec->keylen = keylen;
223 	if (cipher)
224 		ec->contentType = OBJ_nid2obj(NID_pkcs7_data);
225 
226 	return 1;
227 }
228 
229 int
230 CMS_EncryptedData_set1_key(CMS_ContentInfo *cms, const EVP_CIPHER *ciph,
231     const unsigned char *key, size_t keylen)
232 {
233 	CMS_EncryptedContentInfo *ec;
234 
235 	if (!key || !keylen) {
236 		CMSerror(CMS_R_NO_KEY);
237 		return 0;
238 	}
239 	if (ciph) {
240 		cms->d.encryptedData = (CMS_EncryptedData *)ASN1_item_new(&CMS_EncryptedData_it);
241 		if (!cms->d.encryptedData) {
242 			CMSerror(ERR_R_MALLOC_FAILURE);
243 			return 0;
244 		}
245 		cms->contentType = OBJ_nid2obj(NID_pkcs7_encrypted);
246 		cms->d.encryptedData->version = 0;
247 	} else if (OBJ_obj2nid(cms->contentType) != NID_pkcs7_encrypted) {
248 		CMSerror(CMS_R_NOT_ENCRYPTED_DATA);
249 		return 0;
250 	}
251 	ec = cms->d.encryptedData->encryptedContentInfo;
252 
253 	return cms_EncryptedContent_init(ec, ciph, key, keylen);
254 }
255 LCRYPTO_ALIAS(CMS_EncryptedData_set1_key);
256 
257 BIO *
258 cms_EncryptedData_init_bio(CMS_ContentInfo *cms)
259 {
260 	CMS_EncryptedData *enc = cms->d.encryptedData;
261 
262 	if (enc->encryptedContentInfo->cipher && enc->unprotectedAttrs)
263 		enc->version = 2;
264 
265 	return cms_EncryptedContent_init_bio(enc->encryptedContentInfo);
266 }
267