Coverage Report

Created: 2026-09-12 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl41/crypto/cms/cms_dh.c
Line
Count
Source
1
/*
2
 * Copyright 2006-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 <assert.h>
11
#include <openssl/cms.h>
12
#include <openssl/dh.h>
13
#include <openssl/err.h>
14
#include <openssl/core_names.h>
15
#include "internal/sizes.h"
16
#include "crypto/asn1.h"
17
#include "crypto/evp.h"
18
#include "cms_local.h"
19
20
static int dh_cms_set_peerkey(EVP_PKEY_CTX *pctx,
21
    X509_ALGOR *alg, ASN1_BIT_STRING *pubkey)
22
0
{
23
0
    const ASN1_OBJECT *aoid;
24
0
    int atype;
25
0
    const void *aval;
26
0
    ASN1_INTEGER *public_key = NULL;
27
0
    int rv = 0;
28
0
    EVP_PKEY *pkpeer = NULL, *pk = NULL;
29
0
    BIGNUM *bnpub = NULL;
30
0
    const unsigned char *p;
31
0
    unsigned char *buf = NULL;
32
0
    size_t plen;
33
34
0
    X509_ALGOR_get0(&aoid, &atype, &aval, alg);
35
0
    if (OBJ_obj2nid(aoid) != NID_dhpublicnumber)
36
0
        goto err;
37
    /* Only absent parameters allowed in RFC XXXX */
38
0
    if (atype != V_ASN1_UNDEF && atype != V_ASN1_NULL)
39
0
        goto err;
40
41
0
    pk = EVP_PKEY_CTX_get0_pkey(pctx);
42
0
    if (pk == NULL || !EVP_PKEY_is_a(pk, "DHX"))
43
0
        goto err;
44
45
    /* Get public key */
46
0
    plen = ASN1_STRING_get_length(pubkey);
47
0
    if (plen > INT_MAX)
48
0
        goto err;
49
0
    p = ASN1_STRING_get0_data(pubkey);
50
0
    if (p == NULL || plen == 0)
51
0
        goto err;
52
53
0
    if ((public_key = d2i_ASN1_INTEGER(NULL, &p, (int)plen)) == NULL)
54
0
        goto err;
55
    /*
56
     * Pad to full p parameter size as that is checked by
57
     * EVP_PKEY_set1_encoded_public_key()
58
     */
59
0
    plen = EVP_PKEY_get_size(pk);
60
0
    if (plen > INT_MAX)
61
0
        goto err;
62
0
    if ((bnpub = ASN1_INTEGER_to_BN(public_key, NULL)) == NULL)
63
0
        goto err;
64
0
    if ((buf = OPENSSL_malloc(plen)) == NULL)
65
0
        goto err;
66
0
    if (BN_bn2binpad(bnpub, buf, (int)plen) < 0)
67
0
        goto err;
68
69
0
    pkpeer = EVP_PKEY_new();
70
0
    if (pkpeer == NULL
71
0
        || !EVP_PKEY_copy_parameters(pkpeer, pk)
72
0
        || EVP_PKEY_set1_encoded_public_key(pkpeer, buf, (int)plen) <= 0)
73
0
        goto err;
74
75
0
    if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0)
76
0
        rv = 1;
77
0
err:
78
0
    ASN1_INTEGER_free(public_key);
79
0
    BN_free(bnpub);
80
0
    OPENSSL_free(buf);
81
0
    EVP_PKEY_free(pkpeer);
82
0
    return rv;
83
0
}
84
85
static int dh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri)
86
0
{
87
0
    int rv = 0;
88
0
    X509_ALGOR *alg, *kekalg = NULL;
89
0
    ASN1_OCTET_STRING *ukm;
90
0
    const unsigned char *p;
91
0
    unsigned char *dukm = NULL;
92
0
    size_t dukmlen = 0;
93
0
    int keylen;
94
0
    size_t plen;
95
0
    EVP_CIPHER *kekcipher = NULL;
96
0
    EVP_CIPHER_CTX *kekctx;
97
0
    const ASN1_OBJECT *aoid;
98
0
    const void *parameter = NULL;
99
0
    int ptype = 0;
100
0
    char name[OSSL_MAX_NAME_SIZE];
101
102
0
    if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm))
103
0
        goto err;
104
105
0
    X509_ALGOR_get0(&aoid, &ptype, &parameter, alg);
106
107
    /*
108
     * For DH we only have one OID permissible. If ever any more get defined
109
     * we will need something cleverer.
110
     */
111
0
    if (OBJ_obj2nid(aoid) != NID_id_smime_alg_ESDH) {
112
0
        ERR_raise(ERR_LIB_CMS, CMS_R_KDF_PARAMETER_ERROR);
113
0
        goto err;
114
0
    }
115
116
0
    if (EVP_PKEY_CTX_set_dh_kdf_type(pctx, EVP_PKEY_DH_KDF_X9_42) <= 0
117
0
        || EVP_PKEY_CTX_set_dh_kdf_md(pctx, EVP_sha1()) <= 0)
118
0
        goto err;
119
120
0
    if (ptype != V_ASN1_SEQUENCE)
121
0
        goto err;
122
123
0
    p = ASN1_STRING_get0_data(parameter);
124
0
    plen = ASN1_STRING_get_length(parameter);
125
0
    if (plen > INT_MAX)
126
0
        goto err;
127
0
    kekalg = d2i_X509_ALGOR(NULL, &p, (int)plen);
128
0
    if (kekalg == NULL)
129
0
        goto err;
130
0
    kekctx = CMS_RecipientInfo_kari_get0_ctx(ri);
131
0
    if (kekctx == NULL)
132
0
        goto err;
133
134
0
    if (OBJ_obj2txt(name, sizeof(name), kekalg->algorithm, 0) <= 0)
135
0
        goto err;
136
137
0
    kekcipher = EVP_CIPHER_fetch(pctx->libctx, name, pctx->propquery);
138
0
    if (kekcipher == NULL
139
0
        || EVP_CIPHER_get_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
140
0
        goto err;
141
0
    if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL))
142
0
        goto err;
143
0
    if (EVP_CIPHER_asn1_to_param(kekctx, kekalg->parameter) <= 0)
144
0
        goto err;
145
146
0
    keylen = EVP_CIPHER_CTX_get_key_length(kekctx);
147
0
    if (EVP_PKEY_CTX_set_dh_kdf_outlen(pctx, keylen) <= 0)
148
0
        goto err;
149
    /* Use OBJ_nid2obj to ensure we use built in OID that isn't freed */
150
0
    if (EVP_PKEY_CTX_set0_dh_kdf_oid(pctx,
151
0
            OBJ_nid2obj(EVP_CIPHER_get_type(kekcipher)))
152
0
        <= 0)
153
0
        goto err;
154
155
0
    if (ukm != NULL) {
156
0
        dukmlen = ASN1_STRING_get_length(ukm);
157
0
        if (dukmlen > INT_MAX)
158
0
            goto err;
159
0
        dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), (int)dukmlen);
160
0
        if (dukm == NULL)
161
0
            goto err;
162
0
    }
163
164
0
    if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, (int)dukmlen) <= 0)
165
0
        goto err;
166
0
    dukm = NULL;
167
168
0
    rv = 1;
169
0
err:
170
0
    X509_ALGOR_free(kekalg);
171
0
    EVP_CIPHER_free(kekcipher);
172
0
    OPENSSL_free(dukm);
173
0
    return rv;
174
0
}
175
176
static int dh_cms_decrypt(CMS_RecipientInfo *ri)
177
0
{
178
0
    EVP_PKEY_CTX *pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
179
180
0
    if (pctx == NULL)
181
0
        return 0;
182
    /* See if we need to set peer key */
183
0
    if (!EVP_PKEY_CTX_get0_peerkey(pctx)) {
184
0
        X509_ALGOR *alg;
185
0
        ASN1_BIT_STRING *pubkey;
186
187
0
        if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &alg, &pubkey,
188
0
                NULL, NULL, NULL))
189
0
            return 0;
190
0
        if (alg == NULL || pubkey == NULL)
191
0
            return 0;
192
0
        if (!dh_cms_set_peerkey(pctx, alg, pubkey)) {
193
0
            ERR_raise(ERR_LIB_CMS, CMS_R_PEER_KEY_ERROR);
194
0
            return 0;
195
0
        }
196
0
    }
197
    /* Set DH derivation parameters and initialise unwrap context */
198
0
    if (!dh_cms_set_shared_info(pctx, ri)) {
199
0
        ERR_raise(ERR_LIB_CMS, CMS_R_SHARED_INFO_ERROR);
200
0
        return 0;
201
0
    }
202
0
    return 1;
203
0
}
204
205
static int dh_cms_encrypt(CMS_RecipientInfo *ri)
206
0
{
207
0
    EVP_PKEY_CTX *pctx;
208
0
    EVP_PKEY *pkey;
209
0
    EVP_CIPHER_CTX *ctx;
210
0
    int keylen;
211
0
    X509_ALGOR *talg, *wrap_alg = NULL;
212
0
    const ASN1_OBJECT *aoid;
213
0
    ASN1_BIT_STRING *pubkey;
214
0
    ASN1_STRING *wrap_str;
215
0
    ASN1_OCTET_STRING *ukm;
216
0
    unsigned char *penc = NULL, *dukm = NULL;
217
0
    int penclen;
218
0
    size_t dukmlen = 0;
219
0
    int rv = 0;
220
0
    int kdf_type, wrap_nid;
221
0
    const EVP_MD *kdf_md;
222
223
0
    pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
224
0
    if (pctx == NULL)
225
0
        return 0;
226
    /* Get ephemeral key */
227
0
    pkey = EVP_PKEY_CTX_get0_pkey(pctx);
228
0
    if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey,
229
0
            NULL, NULL, NULL))
230
0
        goto err;
231
232
    /* Is everything uninitialised? */
233
0
    X509_ALGOR_get0(&aoid, NULL, NULL, talg);
234
0
    if (aoid == OBJ_nid2obj(NID_undef)) {
235
0
        BIGNUM *bn_pub_key = NULL;
236
0
        ASN1_INTEGER *pubk;
237
238
0
        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &bn_pub_key))
239
0
            goto err;
240
241
0
        pubk = BN_to_ASN1_INTEGER(bn_pub_key, NULL);
242
0
        BN_free(bn_pub_key);
243
0
        if (pubk == NULL)
244
0
            goto err;
245
246
        /* Set the key */
247
0
        penclen = i2d_ASN1_INTEGER(pubk, &penc);
248
0
        ASN1_INTEGER_free(pubk);
249
0
        if (penclen <= 0)
250
0
            goto err;
251
0
        ASN1_STRING_set0(pubkey, penc, penclen);
252
0
        ossl_asn1_bit_string_set_unused_bits(pubkey, 0);
253
254
0
        penc = NULL;
255
0
        (void)X509_ALGOR_set0(talg, OBJ_nid2obj(NID_dhpublicnumber),
256
0
            V_ASN1_UNDEF, NULL); /* cannot fail */
257
0
    }
258
259
    /* See if custom parameters set */
260
0
    kdf_type = EVP_PKEY_CTX_get_dh_kdf_type(pctx);
261
0
    if (kdf_type <= 0 || EVP_PKEY_CTX_get_dh_kdf_md(pctx, &kdf_md) <= 0)
262
0
        goto err;
263
264
0
    if (kdf_type == EVP_PKEY_DH_KDF_NONE) {
265
0
        kdf_type = EVP_PKEY_DH_KDF_X9_42;
266
0
        if (EVP_PKEY_CTX_set_dh_kdf_type(pctx, kdf_type) <= 0)
267
0
            goto err;
268
0
    } else if (kdf_type != EVP_PKEY_DH_KDF_X9_42)
269
        /* Unknown KDF */
270
0
        goto err;
271
0
    if (kdf_md == NULL) {
272
        /* Only SHA1 supported */
273
0
        kdf_md = EVP_sha1();
274
0
        if (EVP_PKEY_CTX_set_dh_kdf_md(pctx, kdf_md) <= 0)
275
0
            goto err;
276
0
    } else if (EVP_MD_get_type(kdf_md) != NID_sha1)
277
        /* Unsupported digest */
278
0
        goto err;
279
280
0
    if (!CMS_RecipientInfo_kari_get0_alg(ri, &talg, &ukm))
281
0
        goto err;
282
283
    /* Get wrap NID */
284
0
    ctx = CMS_RecipientInfo_kari_get0_ctx(ri);
285
0
    wrap_nid = EVP_CIPHER_CTX_get_type(ctx);
286
0
    if (EVP_PKEY_CTX_set0_dh_kdf_oid(pctx, OBJ_nid2obj(wrap_nid)) <= 0)
287
0
        goto err;
288
0
    keylen = EVP_CIPHER_CTX_get_key_length(ctx);
289
290
    /* Package wrap algorithm in an AlgorithmIdentifier */
291
292
0
    wrap_alg = X509_ALGOR_new();
293
0
    if (wrap_alg == NULL)
294
0
        goto err;
295
0
    wrap_alg->algorithm = OBJ_nid2obj(wrap_nid);
296
0
    wrap_alg->parameter = ASN1_TYPE_new();
297
0
    if (wrap_alg->parameter == NULL)
298
0
        goto err;
299
0
    if (EVP_CIPHER_param_to_asn1(ctx, wrap_alg->parameter) <= 0)
300
0
        goto err;
301
0
    if (ASN1_TYPE_get(wrap_alg->parameter) == NID_undef) {
302
0
        ASN1_TYPE_free(wrap_alg->parameter);
303
0
        wrap_alg->parameter = NULL;
304
0
    }
305
306
0
    if (EVP_PKEY_CTX_set_dh_kdf_outlen(pctx, keylen) <= 0)
307
0
        goto err;
308
309
0
    if (ukm != NULL) {
310
0
        dukmlen = ASN1_STRING_get_length(ukm);
311
0
        if (dukmlen > INT_MAX)
312
0
            goto err;
313
0
        dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen);
314
0
        if (dukm == NULL)
315
0
            goto err;
316
0
    }
317
318
0
    if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, (int)dukmlen) <= 0)
319
0
        goto err;
320
0
    dukm = NULL;
321
322
    /*
323
     * Now need to wrap encoding of wrap AlgorithmIdentifier into parameter
324
     * of another AlgorithmIdentifier.
325
     */
326
0
    penc = NULL;
327
0
    penclen = i2d_X509_ALGOR(wrap_alg, &penc);
328
0
    if (penclen <= 0)
329
0
        goto err;
330
0
    wrap_str = ASN1_STRING_new();
331
0
    if (wrap_str == NULL)
332
0
        goto err;
333
0
    ASN1_STRING_set0(wrap_str, penc, penclen);
334
0
    penc = NULL;
335
0
    rv = X509_ALGOR_set0(talg, OBJ_nid2obj(NID_id_smime_alg_ESDH),
336
0
        V_ASN1_SEQUENCE, wrap_str);
337
0
    if (!rv)
338
0
        ASN1_STRING_free(wrap_str);
339
340
0
err:
341
0
    OPENSSL_free(penc);
342
0
    X509_ALGOR_free(wrap_alg);
343
0
    OPENSSL_free(dukm);
344
0
    return rv;
345
0
}
346
347
int ossl_cms_dh_envelope(CMS_RecipientInfo *ri, int decrypt)
348
0
{
349
0
    assert(decrypt == 0 || decrypt == 1);
350
351
0
    if (decrypt == 1)
352
0
        return dh_cms_decrypt(ri);
353
354
0
    if (decrypt == 0)
355
0
        return dh_cms_encrypt(ri);
356
357
0
    ERR_raise(ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
358
0
    return 0;
359
0
}