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