Coverage Report

Created: 2026-04-09 06:50

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