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