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