xref: /dragonfly/crypto/libressl/crypto/cms/cms_enc.c (revision 6f5ec8b5)
1 /* $OpenBSD: cms_enc.c,v 1.21 2022/01/20 10:58:35 inoguchi 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 *
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)) {
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
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
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 *
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