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