Coverage Report

Created: 2025-12-31 06:58

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