Coverage Report

Created: 2026-07-23 06:28

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