Coverage Report

Created: 2025-06-13 06:58

/src/openssl30/crypto/cms/cms_dh.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2006-2023 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
    char name[OSSL_MAX_NAME_SIZE];
92
93
0
    if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm))
94
0
        goto err;
95
96
    /*
97
     * For DH we only have one OID permissible. If ever any more get defined
98
     * we will need something cleverer.
99
     */
100
0
    if (OBJ_obj2nid(alg->algorithm) != NID_id_smime_alg_ESDH) {
101
0
        ERR_raise(ERR_LIB_CMS, CMS_R_KDF_PARAMETER_ERROR);
102
0
        goto err;
103
0
    }
104
105
0
    if (EVP_PKEY_CTX_set_dh_kdf_type(pctx, EVP_PKEY_DH_KDF_X9_42) <= 0
106
0
            || EVP_PKEY_CTX_set_dh_kdf_md(pctx, EVP_sha1()) <= 0)
107
0
        goto err;
108
109
0
    if (alg->parameter->type != V_ASN1_SEQUENCE)
110
0
        goto err;
111
112
0
    p = alg->parameter->value.sequence->data;
113
0
    plen = alg->parameter->value.sequence->length;
114
0
    kekalg = d2i_X509_ALGOR(NULL, &p, plen);
115
0
    if (kekalg == NULL)
116
0
        goto err;
117
0
    kekctx = CMS_RecipientInfo_kari_get0_ctx(ri);
118
0
    if (kekctx == NULL)
119
0
        goto err;
120
121
0
    if (OBJ_obj2txt(name, sizeof(name), kekalg->algorithm, 0) <= 0)
122
0
        goto err;
123
124
0
    kekcipher = EVP_CIPHER_fetch(pctx->libctx, name, pctx->propquery);
125
0
    if (kekcipher == NULL 
126
0
        || EVP_CIPHER_get_mode(kekcipher) != EVP_CIPH_WRAP_MODE)
127
0
        goto err;
128
0
    if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL))
129
0
        goto err;
130
0
    if (EVP_CIPHER_asn1_to_param(kekctx, kekalg->parameter) <= 0)
131
0
        goto err;
132
133
0
    keylen = EVP_CIPHER_CTX_get_key_length(kekctx);
134
0
    if (EVP_PKEY_CTX_set_dh_kdf_outlen(pctx, keylen) <= 0)
135
0
        goto err;
136
    /* Use OBJ_nid2obj to ensure we use built in OID that isn't freed */
137
0
    if (EVP_PKEY_CTX_set0_dh_kdf_oid(pctx,
138
0
                                     OBJ_nid2obj(EVP_CIPHER_get_type(kekcipher)))
139
0
        <= 0)
140
0
        goto err;
141
142
0
    if (ukm != NULL) {
143
0
        dukmlen = ASN1_STRING_length(ukm);
144
0
        dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen);
145
0
        if (dukm == NULL)
146
0
            goto err;
147
0
    }
148
149
0
    if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0)
150
0
        goto err;
151
0
    dukm = NULL;
152
153
0
    rv = 1;
154
0
 err:
155
0
    X509_ALGOR_free(kekalg);
156
0
    EVP_CIPHER_free(kekcipher);
157
0
    OPENSSL_free(dukm);
158
0
    return rv;
159
0
}
160
161
static int dh_cms_decrypt(CMS_RecipientInfo *ri)
162
0
{
163
0
    EVP_PKEY_CTX *pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
164
165
0
    if (pctx == NULL)
166
0
        return 0;
167
    /* See if we need to set peer key */
168
0
    if (!EVP_PKEY_CTX_get0_peerkey(pctx)) {
169
0
        X509_ALGOR *alg;
170
0
        ASN1_BIT_STRING *pubkey;
171
172
0
        if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &alg, &pubkey,
173
0
                                                 NULL, NULL, NULL))
174
0
            return 0;
175
0
        if (alg ==  NULL || pubkey == NULL)
176
0
            return 0;
177
0
        if (!dh_cms_set_peerkey(pctx, alg, pubkey)) {
178
0
            ERR_raise(ERR_LIB_CMS, CMS_R_PEER_KEY_ERROR);
179
0
            return 0;
180
0
        }
181
0
    }
182
    /* Set DH derivation parameters and initialise unwrap context */
183
0
    if (!dh_cms_set_shared_info(pctx, ri)) {
184
0
        ERR_raise(ERR_LIB_CMS, CMS_R_SHARED_INFO_ERROR);
185
0
        return 0;
186
0
    }
187
0
    return 1;
188
0
}
189
190
static int dh_cms_encrypt(CMS_RecipientInfo *ri)
191
0
{
192
0
    EVP_PKEY_CTX *pctx;
193
0
    EVP_PKEY *pkey;
194
0
    EVP_CIPHER_CTX *ctx;
195
0
    int keylen;
196
0
    X509_ALGOR *talg, *wrap_alg = NULL;
197
0
    const ASN1_OBJECT *aoid;
198
0
    ASN1_BIT_STRING *pubkey;
199
0
    ASN1_STRING *wrap_str;
200
0
    ASN1_OCTET_STRING *ukm;
201
0
    unsigned char *penc = NULL, *dukm = NULL;
202
0
    int penclen;
203
0
    size_t dukmlen = 0;
204
0
    int rv = 0;
205
0
    int kdf_type, wrap_nid;
206
0
    const EVP_MD *kdf_md;
207
208
0
    pctx = CMS_RecipientInfo_get0_pkey_ctx(ri);
209
0
    if (pctx == NULL)
210
0
        return 0;
211
    /* Get ephemeral key */
212
0
    pkey = EVP_PKEY_CTX_get0_pkey(pctx);
213
0
    if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey,
214
0
                                             NULL, NULL, NULL))
215
0
        goto err;
216
217
    /* Is everything uninitialised? */
218
0
    X509_ALGOR_get0(&aoid, NULL, NULL, talg);
219
0
    if (aoid == OBJ_nid2obj(NID_undef)) {
220
0
        BIGNUM *bn_pub_key = NULL;
221
0
        ASN1_INTEGER *pubk;
222
223
0
        if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, &bn_pub_key))
224
0
            goto err;
225
226
0
        pubk = BN_to_ASN1_INTEGER(bn_pub_key, NULL);
227
0
        BN_free(bn_pub_key);
228
0
        if (pubk == NULL)
229
0
            goto err;
230
231
        /* Set the key */
232
0
        penclen = i2d_ASN1_INTEGER(pubk, &penc);
233
0
        ASN1_INTEGER_free(pubk);
234
0
        if (penclen <= 0)
235
0
            goto err;
236
0
        ASN1_STRING_set0(pubkey, penc, penclen);
237
0
        pubkey->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
238
0
        pubkey->flags |= ASN1_STRING_FLAG_BITS_LEFT;
239
240
0
        penc = NULL;
241
0
        X509_ALGOR_set0(talg, OBJ_nid2obj(NID_dhpublicnumber),
242
0
                        V_ASN1_UNDEF, NULL);
243
0
    }
244
245
    /* See if custom parameters set */
246
0
    kdf_type = EVP_PKEY_CTX_get_dh_kdf_type(pctx);
247
0
    if (kdf_type <= 0 || EVP_PKEY_CTX_get_dh_kdf_md(pctx, &kdf_md) <= 0)
248
0
        goto err;
249
250
0
    if (kdf_type == EVP_PKEY_DH_KDF_NONE) {
251
0
        kdf_type = EVP_PKEY_DH_KDF_X9_42;
252
0
        if (EVP_PKEY_CTX_set_dh_kdf_type(pctx, kdf_type) <= 0)
253
0
            goto err;
254
0
    } else if (kdf_type != EVP_PKEY_DH_KDF_X9_42)
255
        /* Unknown KDF */
256
0
        goto err;
257
0
    if (kdf_md == NULL) {
258
        /* Only SHA1 supported */
259
0
        kdf_md = EVP_sha1();
260
0
        if (EVP_PKEY_CTX_set_dh_kdf_md(pctx, kdf_md) <= 0)
261
0
            goto err;
262
0
    } else if (EVP_MD_get_type(kdf_md) != NID_sha1)
263
        /* Unsupported digest */
264
0
        goto err;
265
266
0
    if (!CMS_RecipientInfo_kari_get0_alg(ri, &talg, &ukm))
267
0
        goto err;
268
269
    /* Get wrap NID */
270
0
    ctx = CMS_RecipientInfo_kari_get0_ctx(ri);
271
0
    wrap_nid = EVP_CIPHER_CTX_get_type(ctx);
272
0
    if (EVP_PKEY_CTX_set0_dh_kdf_oid(pctx, OBJ_nid2obj(wrap_nid)) <= 0)
273
0
        goto err;
274
0
    keylen = EVP_CIPHER_CTX_get_key_length(ctx);
275
276
    /* Package wrap algorithm in an AlgorithmIdentifier */
277
278
0
    wrap_alg = X509_ALGOR_new();
279
0
    if (wrap_alg == NULL)
280
0
        goto err;
281
0
    wrap_alg->algorithm = OBJ_nid2obj(wrap_nid);
282
0
    wrap_alg->parameter = ASN1_TYPE_new();
283
0
    if (wrap_alg->parameter == NULL)
284
0
        goto err;
285
0
    if (EVP_CIPHER_param_to_asn1(ctx, wrap_alg->parameter) <= 0)
286
0
        goto err;
287
0
    if (ASN1_TYPE_get(wrap_alg->parameter) == NID_undef) {
288
0
        ASN1_TYPE_free(wrap_alg->parameter);
289
0
        wrap_alg->parameter = NULL;
290
0
    }
291
292
0
    if (EVP_PKEY_CTX_set_dh_kdf_outlen(pctx, keylen) <= 0)
293
0
        goto err;
294
295
0
    if (ukm != NULL) {
296
0
        dukmlen = ASN1_STRING_length(ukm);
297
0
        dukm = OPENSSL_memdup(ASN1_STRING_get0_data(ukm), dukmlen);
298
0
        if (dukm == NULL)
299
0
            goto err;
300
0
    }
301
302
0
    if (EVP_PKEY_CTX_set0_dh_kdf_ukm(pctx, dukm, dukmlen) <= 0)
303
0
        goto err;
304
0
    dukm = NULL;
305
306
    /*
307
     * Now need to wrap encoding of wrap AlgorithmIdentifier into parameter
308
     * of another AlgorithmIdentifier.
309
     */
310
0
    penc = NULL;
311
0
    penclen = i2d_X509_ALGOR(wrap_alg, &penc);
312
0
    if (penclen <= 0)
313
0
        goto err;
314
0
    wrap_str = ASN1_STRING_new();
315
0
    if (wrap_str == NULL)
316
0
        goto err;
317
0
    ASN1_STRING_set0(wrap_str, penc, penclen);
318
0
    penc = NULL;
319
0
    rv = X509_ALGOR_set0(talg, OBJ_nid2obj(NID_id_smime_alg_ESDH),
320
0
                         V_ASN1_SEQUENCE, wrap_str);
321
0
    if (!rv)
322
0
        ASN1_STRING_free(wrap_str);
323
324
0
 err:
325
0
    OPENSSL_free(penc);
326
0
    X509_ALGOR_free(wrap_alg);
327
0
    OPENSSL_free(dukm);
328
0
    return rv;
329
0
}
330
331
int ossl_cms_dh_envelope(CMS_RecipientInfo *ri, int decrypt)
332
0
{
333
0
    assert(decrypt == 0 || decrypt == 1);
334
335
0
    if (decrypt == 1)
336
0
        return dh_cms_decrypt(ri);
337
338
0
    if (decrypt == 0)
339
0
        return dh_cms_encrypt(ri);
340
341
0
    ERR_raise(ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE);
342
0
    return 0;
343
0
}