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