1 /*
2  * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 #include <assert.h>
11 #include <limits.h>
12 #include <openssl/cms.h>
13 #include <openssl/core_names.h>
14 #include <openssl/err.h>
15 #include <openssl/decoder.h>
16 #include "internal/sizes.h"
17 #include "crypto/asn1.h"
18 #include "crypto/evp.h"
19 #include "cms_local.h"
20 
kem_cms_decrypt(CMS_RecipientInfo * ri)21 static int kem_cms_decrypt(CMS_RecipientInfo *ri)
22 {
23     uint32_t *kekLength;
24     X509_ALGOR *wrap;
25     EVP_PKEY_CTX *pctx;
26     EVP_CIPHER_CTX *kekctx;
27     uint32_t cipher_length;
28     char name[OSSL_MAX_NAME_SIZE];
29     EVP_CIPHER *kekcipher = NULL;
30     int rv = 0;
31 
32     if (!ossl_cms_RecipientInfo_kemri_get0_alg(ri, &kekLength, &wrap))
33         goto err;
34 
35     pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
36     if (pctx == NULL)
37         goto err;
38 
39     kekctx = CMS_RecipientInfo_kemri_get0_ctx(ri);
40     if (kekctx == NULL)
41         goto err;
42 
43     OBJ_obj2txt(name, sizeof(name), wrap->algorithm, 0);
44     kekcipher = EVP_CIPHER_fetch(pctx->libctx, name, pctx->propquery);
45     if (kekcipher == NULL || EVP_CIPHER_get_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
46         goto err;
47     if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL))
48         goto err;
49     if (EVP_CIPHER_asn1_to_param(kekctx, wrap->parameter) <= 0)
50         goto err;
51 
52     cipher_length = EVP_CIPHER_CTX_get_key_length(kekctx);
53     if (cipher_length != *kekLength) {
54         ERR_raise(ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH);
55         goto err;
56     }
57 
58     rv = 1;
59 err:
60     EVP_CIPHER_free(kekcipher);
61     return rv;
62 }
63 
kem_cms_encrypt(CMS_RecipientInfo * ri)64 static int kem_cms_encrypt(CMS_RecipientInfo *ri)
65 {
66     uint32_t *kekLength;
67     X509_ALGOR *wrap;
68     X509_ALGOR *kdf;
69     EVP_PKEY_CTX *pctx;
70     EVP_PKEY *pkey;
71     int security_bits;
72     const ASN1_OBJECT *kdf_obj = NULL;
73     unsigned char kemri_x509_algor[OSSL_MAX_ALGORITHM_ID_SIZE];
74     OSSL_PARAM params[2];
75     X509_ALGOR *x509_algor = NULL;
76     EVP_CIPHER_CTX *kekctx;
77     int wrap_nid;
78     int rv = 0;
79 
80     if (!ossl_cms_RecipientInfo_kemri_get0_alg(ri, &kekLength, &wrap))
81         goto err;
82 
83     kdf = CMS_RecipientInfo_kemri_get0_kdf_alg(ri);
84     if (kdf == NULL)
85         goto err;
86 
87     pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
88     if (pctx == NULL)
89         goto err;
90 
91     pkey = EVP_PKEY_CTX_get0_pkey(pctx);
92     if (pkey == NULL)
93         goto err;
94 
95     security_bits = EVP_PKEY_get_security_bits(pkey);
96     if (security_bits == 0)
97         goto err;
98 
99     X509_ALGOR_get0(&kdf_obj, NULL, NULL, kdf);
100     if (kdf_obj == NULL || OBJ_obj2nid(kdf_obj) == NID_undef) {
101         /*
102          * If the KDF OID hasn't already been set, then query the provider
103          * for a default KDF.
104          */
105         params[0] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_CMS_KEMRI_KDF_ALGORITHM,
106                                                       kemri_x509_algor, sizeof(kemri_x509_algor));
107         params[1] = OSSL_PARAM_construct_end();
108         if (!EVP_PKEY_get_params(pkey, params))
109             goto err;
110         if (OSSL_PARAM_modified(&params[0])) {
111             const unsigned char *p = kemri_x509_algor;
112 
113             x509_algor = d2i_X509_ALGOR(NULL, &p, (long)params[0].return_size);
114             if (x509_algor == NULL)
115                 goto err;
116             if (!X509_ALGOR_copy(kdf, x509_algor))
117                 goto err;
118         } else {
119             if (!X509_ALGOR_set0(kdf, OBJ_nid2obj(NID_HKDF_SHA256), V_ASN1_UNDEF, NULL))
120                 return 0;
121         }
122     }
123 
124     /* Get wrap NID */
125     kekctx = CMS_RecipientInfo_kemri_get0_ctx(ri);
126     if (kekctx == NULL)
127         goto err;
128     *kekLength = EVP_CIPHER_CTX_get_key_length(kekctx);
129     wrap_nid = EVP_CIPHER_CTX_get_type(kekctx);
130 
131     /* Package wrap algorithm in an AlgorithmIdentifier */
132     ASN1_OBJECT_free(wrap->algorithm);
133     ASN1_TYPE_free(wrap->parameter);
134     wrap->algorithm = OBJ_nid2obj(wrap_nid);
135     wrap->parameter = ASN1_TYPE_new();
136     if (wrap->parameter == NULL)
137         goto err;
138     if (EVP_CIPHER_param_to_asn1(kekctx, wrap->parameter) <= 0)
139         goto err;
140     if (ASN1_TYPE_get(wrap->parameter) == NID_undef) {
141         ASN1_TYPE_free(wrap->parameter);
142         wrap->parameter = NULL;
143     }
144 
145     rv = 1;
146 err:
147     X509_ALGOR_free(x509_algor);
148     return rv;
149 }
150 
ossl_cms_kem_envelope(CMS_RecipientInfo * ri,int decrypt)151 int ossl_cms_kem_envelope(CMS_RecipientInfo *ri, int decrypt)
152 {
153     assert(decrypt == 0 || decrypt == 1);
154 
155     if (decrypt == 1)
156         return kem_cms_decrypt(ri);
157 
158     if (decrypt == 0)
159         return kem_cms_encrypt(ri);
160 
161     ERR_raise(ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
162     return 0;
163 }
164