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 <openssl/cms.h>
11 #include <openssl/core_names.h>
12 #include <openssl/crypto.h>
13 #include <openssl/err.h>
14 #include <openssl/evp.h>
15 #include <openssl/kdf.h>
16 #include <openssl/x509.h>
17 #include "cms_local.h"
18 #include "crypto/evp.h"
19 #include "internal/sizes.h"
20 
21 /* KEM Recipient Info (KEMRI) routines */
22 
ossl_cms_RecipientInfo_kemri_get0_alg(CMS_RecipientInfo * ri,uint32_t ** pkekLength,X509_ALGOR ** pwrap)23 int ossl_cms_RecipientInfo_kemri_get0_alg(CMS_RecipientInfo *ri,
24                                           uint32_t **pkekLength,
25                                           X509_ALGOR **pwrap)
26 {
27     if (ri->type != CMS_RECIPINFO_KEM) {
28         ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
29         return 0;
30     }
31     if (pkekLength)
32         *pkekLength = &ri->d.ori->d.kemri->kekLength;
33     if (pwrap)
34         *pwrap = ri->d.ori->d.kemri->wrap;
35     return 1;
36 }
37 
CMS_RecipientInfo_kemri_cert_cmp(CMS_RecipientInfo * ri,X509 * cert)38 int CMS_RecipientInfo_kemri_cert_cmp(CMS_RecipientInfo *ri, X509 *cert)
39 {
40     if (ri->type != CMS_RECIPINFO_KEM) {
41         ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
42         return -2;
43     }
44     return ossl_cms_SignerIdentifier_cert_cmp(ri->d.ori->d.kemri->rid, cert);
45 }
46 
CMS_RecipientInfo_kemri_set0_pkey(CMS_RecipientInfo * ri,EVP_PKEY * pk)47 int CMS_RecipientInfo_kemri_set0_pkey(CMS_RecipientInfo *ri, EVP_PKEY *pk)
48 {
49     EVP_PKEY_CTX *pctx = NULL;
50     CMS_KEMRecipientInfo *kemri;
51 
52     if (ri->type != CMS_RECIPINFO_KEM) {
53         ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
54         return 0;
55     }
56 
57     kemri = ri->d.ori->d.kemri;
58 
59     EVP_PKEY_CTX_free(kemri->pctx);
60     kemri->pctx = NULL;
61 
62     if (pk != NULL) {
63         pctx = EVP_PKEY_CTX_new_from_pkey(ossl_cms_ctx_get0_libctx(kemri->cms_ctx), pk,
64                                           ossl_cms_ctx_get0_propq(kemri->cms_ctx));
65         if (pctx == NULL || EVP_PKEY_decapsulate_init(pctx, NULL) <= 0)
66             goto err;
67 
68         kemri->pctx = pctx;
69     }
70 
71     return 1;
72 err:
73     EVP_PKEY_CTX_free(pctx);
74     return 0;
75 }
76 
77 /* Initialise a kemri based on passed certificate and key */
78 
ossl_cms_RecipientInfo_kemri_init(CMS_RecipientInfo * ri,X509 * recip,EVP_PKEY * recipPubKey,unsigned int flags,const CMS_CTX * ctx)79 int ossl_cms_RecipientInfo_kemri_init(CMS_RecipientInfo *ri, X509 *recip,
80                                       EVP_PKEY *recipPubKey, unsigned int flags,
81                                       const CMS_CTX *ctx)
82 {
83     CMS_OtherRecipientInfo *ori;
84     CMS_KEMRecipientInfo *kemri;
85     int idtype;
86     X509_PUBKEY *x_pubkey;
87     X509_ALGOR *x_alg;
88 
89     ri->d.ori = M_ASN1_new_of(CMS_OtherRecipientInfo);
90     if (ri->d.ori == NULL)
91         return 0;
92     ri->encoded_type = CMS_RECIPINFO_OTHER;
93     ri->type = CMS_RECIPINFO_KEM;
94 
95     ori = ri->d.ori;
96     ori->oriType = OBJ_nid2obj(NID_id_smime_ori_kem);
97     if (ori->oriType == NULL)
98         return 0;
99     ori->d.kemri = M_ASN1_new_of(CMS_KEMRecipientInfo);
100     if (ori->d.kemri == NULL)
101         return 0;
102 
103     kemri = ori->d.kemri;
104     kemri->version = 0;
105     kemri->cms_ctx = ctx;
106 
107     /*
108      * Not a typo: RecipientIdentifier and SignerIdentifier are the same
109      * structure.
110      */
111 
112     idtype = (flags & CMS_USE_KEYID) ? CMS_RECIPINFO_KEYIDENTIFIER : CMS_RECIPINFO_ISSUER_SERIAL;
113     if (!ossl_cms_set1_SignerIdentifier(kemri->rid, recip, idtype, ctx))
114         return 0;
115 
116     x_pubkey = X509_get_X509_PUBKEY(recip);
117     if (x_pubkey == NULL)
118         return 0;
119     if (!X509_PUBKEY_get0_param(NULL, NULL, NULL, &x_alg, x_pubkey))
120         return 0;
121     if (!X509_ALGOR_copy(kemri->kem, x_alg))
122         return 0;
123 
124     kemri->pctx = EVP_PKEY_CTX_new_from_pkey(ossl_cms_ctx_get0_libctx(ctx),
125                                              recipPubKey,
126                                              ossl_cms_ctx_get0_propq(ctx));
127     if (kemri->pctx == NULL)
128         return 0;
129     if (EVP_PKEY_encapsulate_init(kemri->pctx, NULL) <= 0)
130         return 0;
131 
132     return 1;
133 }
134 
CMS_RecipientInfo_kemri_get0_ctx(CMS_RecipientInfo * ri)135 EVP_CIPHER_CTX *CMS_RecipientInfo_kemri_get0_ctx(CMS_RecipientInfo *ri)
136 {
137     if (ri->type == CMS_RECIPINFO_KEM)
138         return ri->d.ori->d.kemri->ctx;
139     return NULL;
140 }
141 
CMS_RecipientInfo_kemri_get0_kdf_alg(CMS_RecipientInfo * ri)142 X509_ALGOR *CMS_RecipientInfo_kemri_get0_kdf_alg(CMS_RecipientInfo *ri)
143 {
144     if (ri->type == CMS_RECIPINFO_KEM)
145         return ri->d.ori->d.kemri->kdf;
146     return NULL;
147 }
148 
CMS_RecipientInfo_kemri_set_ukm(CMS_RecipientInfo * ri,const unsigned char * ukm,int ukmLength)149 int CMS_RecipientInfo_kemri_set_ukm(CMS_RecipientInfo *ri,
150                                     const unsigned char *ukm,
151                                     int ukmLength)
152 {
153     CMS_KEMRecipientInfo *kemri;
154     ASN1_OCTET_STRING *ukm_str;
155 
156     if (ri->type != CMS_RECIPINFO_KEM) {
157         ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
158         return 0;
159     }
160 
161     if (ukm == NULL && ukmLength != 0) {
162         ERR_raise(ERR_LIB_CMS, ERR_R_PASSED_INVALID_ARGUMENT);
163         return 0;
164     }
165 
166     kemri = ri->d.ori->d.kemri;
167 
168     ukm_str = ASN1_OCTET_STRING_new();
169     if (ukm_str == NULL)
170         return 0;
171     if (!ASN1_OCTET_STRING_set(ukm_str, ukm, ukmLength)) {
172         ASN1_OCTET_STRING_free(ukm_str);
173         return 0;
174     }
175     ASN1_OCTET_STRING_free(kemri->ukm);
176     kemri->ukm = ukm_str;
177     return 1;
178 }
179 
create_kdf_ctx(CMS_KEMRecipientInfo * kemri)180 static EVP_KDF_CTX *create_kdf_ctx(CMS_KEMRecipientInfo *kemri)
181 {
182     const ASN1_OBJECT *kdf_oid;
183     int ptype;
184     char kdf_alg[OSSL_MAX_NAME_SIZE];
185     EVP_KDF *kdf = NULL;
186     EVP_KDF_CTX *kctx = NULL;
187 
188     /*
189      * KDFs with algorithm identifier parameters are not supported yet. To
190      * support this, EVP_KDF_CTX_set_algor_params from
191      * `doc/designs/passing-algorithmidentifier-parameters.md` needs to be
192      * implemented.
193      */
194     X509_ALGOR_get0(&kdf_oid, &ptype, NULL, kemri->kdf);
195     if (ptype != V_ASN1_UNDEF && ptype != V_ASN1_NULL) {
196         ERR_raise(ERR_LIB_CMS, CMS_R_UNSUPPORTED_KDF_ALGORITHM);
197         goto err;
198     }
199     if (OBJ_obj2txt(kdf_alg, sizeof(kdf_alg), kdf_oid, 1) < 0)
200         goto err;
201 
202     kdf = EVP_KDF_fetch(ossl_cms_ctx_get0_libctx(kemri->cms_ctx), kdf_alg,
203                         ossl_cms_ctx_get0_propq(kemri->cms_ctx));
204     if (kdf == NULL)
205         goto err;
206 
207     kctx = EVP_KDF_CTX_new(kdf);
208 err:
209     EVP_KDF_free(kdf);
210     return kctx;
211 }
212 
kdf_derive(unsigned char * kek,size_t keklen,const unsigned char * ss,size_t sslen,CMS_KEMRecipientInfo * kemri)213 static int kdf_derive(unsigned char *kek, size_t keklen,
214                       const unsigned char *ss, size_t sslen,
215                       CMS_KEMRecipientInfo *kemri)
216 {
217     EVP_KDF_CTX *kctx = NULL;
218     OSSL_PARAM params[3];
219     unsigned char *infoder = NULL;
220     int infolen = 0;
221     int rv = 0;
222 
223     infolen = CMS_CMSORIforKEMOtherInfo_encode(&infoder, kemri->wrap, kemri->ukm,
224                                                kemri->kekLength);
225     if (infolen <= 0)
226         goto err;
227 
228     kctx = create_kdf_ctx(kemri);
229     if (kctx == NULL)
230         goto err;
231 
232     params[0] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
233                                                   (unsigned char *)ss, sslen);
234     params[1] = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
235                                                   (char *)infoder, infolen);
236     params[2] = OSSL_PARAM_construct_end();
237 
238     if (EVP_KDF_derive(kctx, kek, keklen, params) <= 0)
239         goto err;
240 
241     rv = 1;
242 err:
243     OPENSSL_free(infoder);
244     EVP_KDF_CTX_free(kctx);
245 
246     return rv;
247 }
248 
249 /*
250  * Derive KEK and decrypt/encrypt with it to produce either the original CEK
251  * or the encrypted CEK.
252  */
253 
cms_kek_cipher(unsigned char ** pout,size_t * poutlen,const unsigned char * ss,size_t sslen,const unsigned char * in,size_t inlen,CMS_KEMRecipientInfo * kemri,int enc)254 static int cms_kek_cipher(unsigned char **pout, size_t *poutlen,
255                           const unsigned char *ss, size_t sslen,
256                           const unsigned char *in, size_t inlen,
257                           CMS_KEMRecipientInfo *kemri, int enc)
258 {
259     /* Key encryption key */
260     unsigned char kek[EVP_MAX_KEY_LENGTH];
261     size_t keklen = kemri->kekLength;
262     unsigned char *out = NULL;
263     int outlen = 0;
264     int rv = 0;
265 
266     if (keklen > sizeof(kek)) {
267         ERR_raise(ERR_LIB_CMS, CMS_R_INVALID_KEY_LENGTH);
268         return 0;
269     }
270 
271     if (!kdf_derive(kek, keklen, ss, sslen, kemri))
272         goto err;
273 
274     /* Set KEK in context */
275     if (!EVP_CipherInit_ex(kemri->ctx, NULL, NULL, kek, NULL, enc))
276         goto err;
277     /* obtain output length of ciphered key */
278     if (!EVP_CipherUpdate(kemri->ctx, NULL, &outlen, in, (int)inlen))
279         goto err;
280     out = OPENSSL_malloc(outlen);
281     if (out == NULL)
282         goto err;
283     if (!EVP_CipherUpdate(kemri->ctx, out, &outlen, in, (int)inlen))
284         goto err;
285     *pout = out;
286     out = NULL;
287     *poutlen = (size_t)outlen;
288 
289     rv = 1;
290 err:
291     OPENSSL_free(out);
292     OPENSSL_cleanse(kek, sizeof(kek));
293     EVP_CIPHER_CTX_reset(kemri->ctx);
294     EVP_PKEY_CTX_free(kemri->pctx);
295     kemri->pctx = NULL;
296     return rv;
297 }
298 
299 /* Encrypt content key in KEM recipient info */
300 
ossl_cms_RecipientInfo_kemri_encrypt(const CMS_ContentInfo * cms,CMS_RecipientInfo * ri)301 int ossl_cms_RecipientInfo_kemri_encrypt(const CMS_ContentInfo *cms,
302                                          CMS_RecipientInfo *ri)
303 {
304     CMS_KEMRecipientInfo *kemri;
305     CMS_EncryptedContentInfo *ec;
306     unsigned char *kem_ct = NULL;
307     size_t kem_ct_len;
308     unsigned char *kem_secret = NULL;
309     size_t kem_secret_len = 0;
310     unsigned char *enckey;
311     size_t enckeylen;
312     int rv = 0;
313 
314     if (ri->type != CMS_RECIPINFO_KEM) {
315         ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
316         return 0;
317     }
318 
319     kemri = ri->d.ori->d.kemri;
320 
321     ec = ossl_cms_get0_env_enc_content(cms);
322     /* Initialise wrap algorithm parameters */
323     if (!ossl_cms_RecipientInfo_wrap_init(ri, ec->cipher))
324         return 0;
325 
326     /* Initialise KDF algorithm */
327     if (!ossl_cms_env_asn1_ctrl(ri, 0))
328         return 0;
329 
330     if (EVP_PKEY_encapsulate(kemri->pctx, NULL, &kem_ct_len, NULL, &kem_secret_len) <= 0)
331         return 0;
332     kem_ct = OPENSSL_malloc(kem_ct_len);
333     kem_secret = OPENSSL_malloc(kem_secret_len);
334     if (kem_ct == NULL || kem_secret == NULL)
335         goto err;
336 
337     if (EVP_PKEY_encapsulate(kemri->pctx, kem_ct, &kem_ct_len, kem_secret, &kem_secret_len) <= 0)
338         goto err;
339 
340     ASN1_STRING_set0(kemri->kemct, kem_ct, (int)kem_ct_len);
341     kem_ct = NULL;
342 
343     if (!cms_kek_cipher(&enckey, &enckeylen, kem_secret, kem_secret_len, ec->key, ec->keylen,
344                         kemri, 1))
345         goto err;
346     ASN1_STRING_set0(kemri->encryptedKey, enckey, (int)enckeylen);
347 
348     rv = 1;
349 err:
350     OPENSSL_free(kem_ct);
351     OPENSSL_clear_free(kem_secret, kem_secret_len);
352     return rv;
353 }
354 
ossl_cms_RecipientInfo_kemri_decrypt(const CMS_ContentInfo * cms,CMS_RecipientInfo * ri)355 int ossl_cms_RecipientInfo_kemri_decrypt(const CMS_ContentInfo *cms,
356                                          CMS_RecipientInfo *ri)
357 {
358     CMS_KEMRecipientInfo *kemri;
359     CMS_EncryptedContentInfo *ec;
360     const unsigned char *kem_ct = NULL;
361     size_t kem_ct_len;
362     unsigned char *kem_secret = NULL;
363     size_t kem_secret_len = 0;
364     unsigned char *enckey = NULL;
365     size_t enckeylen;
366     unsigned char *cek = NULL;
367     size_t ceklen;
368     int ret = 0;
369 
370     if (ri->type != CMS_RECIPINFO_KEM) {
371         ERR_raise(ERR_LIB_CMS, CMS_R_NOT_KEM);
372         return 0;
373     }
374 
375     kemri = ri->d.ori->d.kemri;
376 
377     ec = ossl_cms_get0_env_enc_content(cms);
378 
379     if (kemri->pctx == NULL) {
380         ERR_raise(ERR_LIB_CMS, CMS_R_NO_PRIVATE_KEY);
381         return 0;
382     }
383 
384     /* Setup all parameters to derive KEK */
385     if (!ossl_cms_env_asn1_ctrl(ri, 1))
386         goto err;
387 
388     kem_ct = ASN1_STRING_get0_data(kemri->kemct);
389     kem_ct_len = ASN1_STRING_length(kemri->kemct);
390 
391     if (EVP_PKEY_decapsulate(kemri->pctx, NULL, &kem_secret_len, kem_ct, kem_ct_len) <= 0)
392         return 0;
393     kem_secret = OPENSSL_malloc(kem_secret_len);
394     if (kem_secret == NULL)
395         goto err;
396 
397     if (EVP_PKEY_decapsulate(kemri->pctx, kem_secret, &kem_secret_len, kem_ct, kem_ct_len) <= 0)
398         goto err;
399 
400     /* Attempt to decrypt CEK */
401     enckeylen = kemri->encryptedKey->length;
402     enckey = kemri->encryptedKey->data;
403     if (!cms_kek_cipher(&cek, &ceklen, kem_secret, kem_secret_len, enckey, enckeylen, kemri, 0))
404         goto err;
405     ec = ossl_cms_get0_env_enc_content(cms);
406     OPENSSL_clear_free(ec->key, ec->keylen);
407     ec->key = cek;
408     ec->keylen = ceklen;
409 
410     ret = 1;
411 err:
412     OPENSSL_clear_free(kem_secret, kem_secret_len);
413     return ret;
414 }
415