]>
Commit | Line | Data |
---|---|---|
0b3a4ef2 | 1 | /* |
fecb3aae | 2 | * Copyright 2006-2022 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 MC |
11 | #include <openssl/cms.h> |
12 | #include <openssl/err.h> | |
13 | #include <openssl/decoder.h> | |
ad57a13b | 14 | #include "internal/sizes.h" |
7c310e87 | 15 | #include "crypto/asn1.h" |
0b3a4ef2 | 16 | #include "crypto/evp.h" |
ad57a13b | 17 | #include "cms_local.h" |
0b3a4ef2 | 18 | |
0b3a4ef2 | 19 | static EVP_PKEY *pkey_type2param(int ptype, const void *pval, |
b4250010 | 20 | OSSL_LIB_CTX *libctx, const char *propq) |
0b3a4ef2 MC |
21 | { |
22 | EVP_PKEY *pkey = NULL; | |
23 | EVP_PKEY_CTX *pctx = NULL; | |
8dff167d | 24 | OSSL_DECODER_CTX *ctx = NULL; |
0b3a4ef2 MC |
25 | |
26 | if (ptype == V_ASN1_SEQUENCE) { | |
27 | const ASN1_STRING *pstr = pval; | |
28 | const unsigned char *pm = pstr->data; | |
4f2abe43 | 29 | size_t pmlen = (size_t)pstr->length; |
4f2abe43 | 30 | int selection = OSSL_KEYMGMT_SELECT_ALL_PARAMETERS; |
0b3a4ef2 | 31 | |
fe75766c TM |
32 | ctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL, "EC", |
33 | selection, libctx, propq); | |
9ab7fe48 MC |
34 | if (ctx == NULL) |
35 | goto err; | |
36 | ||
8dff167d P |
37 | if (!OSSL_DECODER_from_data(ctx, &pm, &pmlen)) { |
38 | ERR_raise(ERR_LIB_CMS, CMS_R_DECODE_ERROR); | |
39 | goto err; | |
60d13c8f | 40 | } |
0b3a4ef2 | 41 | OSSL_DECODER_CTX_free(ctx); |
dfb0b8d6 | 42 | return pkey; |
0b3a4ef2 MC |
43 | } else if (ptype == V_ASN1_OBJECT) { |
44 | const ASN1_OBJECT *poid = pval; | |
ad57a13b | 45 | char groupname[OSSL_MAX_NAME_SIZE]; |
0b3a4ef2 | 46 | |
9ab7fe48 | 47 | /* type == V_ASN1_OBJECT => the parameters are given by an asn1 OID */ |
0b3a4ef2 | 48 | pctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", propq); |
9ab7fe48 | 49 | if (pctx == NULL || EVP_PKEY_paramgen_init(pctx) <= 0) |
0b3a4ef2 | 50 | goto err; |
2349d7ba | 51 | if (OBJ_obj2txt(groupname, sizeof(groupname), poid, 0) <= 0 |
56876ae9 | 52 | || EVP_PKEY_CTX_set_group_name(pctx, groupname) <= 0) { |
9311d0c4 | 53 | ERR_raise(ERR_LIB_CMS, CMS_R_DECODE_ERROR); |
0b3a4ef2 MC |
54 | goto err; |
55 | } | |
9ab7fe48 MC |
56 | if (EVP_PKEY_paramgen(pctx, &pkey) <= 0) |
57 | goto err; | |
dfb0b8d6 P |
58 | EVP_PKEY_CTX_free(pctx); |
59 | return pkey; | |
0b3a4ef2 MC |
60 | } |
61 | ||
dfb0b8d6 P |
62 | ERR_raise(ERR_LIB_CMS, CMS_R_DECODE_ERROR); |
63 | return NULL; | |
0b3a4ef2 MC |
64 | |
65 | err: | |
66 | EVP_PKEY_free(pkey); | |
67 | EVP_PKEY_CTX_free(pctx); | |
8dff167d | 68 | OSSL_DECODER_CTX_free(ctx); |
0b3a4ef2 MC |
69 | return NULL; |
70 | } | |
71 | ||
72 | static int ecdh_cms_set_peerkey(EVP_PKEY_CTX *pctx, | |
73 | X509_ALGOR *alg, ASN1_BIT_STRING *pubkey) | |
74 | { | |
75 | const ASN1_OBJECT *aoid; | |
76 | int atype; | |
77 | const void *aval; | |
78 | int rv = 0; | |
79 | EVP_PKEY *pkpeer = NULL; | |
80 | const unsigned char *p; | |
81 | int plen; | |
82 | ||
83 | X509_ALGOR_get0(&aoid, &atype, &aval, alg); | |
84 | if (OBJ_obj2nid(aoid) != NID_X9_62_id_ecPublicKey) | |
85 | goto err; | |
9ab7fe48 | 86 | |
0b3a4ef2 MC |
87 | /* If absent parameters get group from main key */ |
88 | if (atype == V_ASN1_UNDEF || atype == V_ASN1_NULL) { | |
89 | EVP_PKEY *pk; | |
9ab7fe48 | 90 | |
0b3a4ef2 MC |
91 | pk = EVP_PKEY_CTX_get0_pkey(pctx); |
92 | if (pk == NULL) | |
93 | goto err; | |
94 | ||
95 | pkpeer = EVP_PKEY_new(); | |
96 | if (pkpeer == NULL) | |
97 | goto err; | |
98 | if (!EVP_PKEY_copy_parameters(pkpeer, pk)) | |
99 | goto err; | |
100 | } else { | |
0b3a4ef2 | 101 | pkpeer = pkey_type2param(atype, aval, |
29000e43 MC |
102 | EVP_PKEY_CTX_get0_libctx(pctx), |
103 | EVP_PKEY_CTX_get0_propq(pctx)); | |
0b3a4ef2 MC |
104 | if (pkpeer == NULL) |
105 | goto err; | |
106 | } | |
107 | /* We have parameters now set public key */ | |
108 | plen = ASN1_STRING_length(pubkey); | |
109 | p = ASN1_STRING_get0_data(pubkey); | |
110 | if (p == NULL || plen == 0) | |
111 | goto err; | |
112 | ||
5ac8fb58 | 113 | if (!EVP_PKEY_set1_encoded_public_key(pkpeer, p, plen)) |
0b3a4ef2 MC |
114 | goto err; |
115 | ||
116 | if (EVP_PKEY_derive_set_peer(pctx, pkpeer) > 0) | |
117 | rv = 1; | |
118 | err: | |
119 | EVP_PKEY_free(pkpeer); | |
120 | return rv; | |
121 | } | |
122 | ||
123 | /* Set KDF parameters based on KDF NID */ | |
124 | static int ecdh_cms_set_kdf_param(EVP_PKEY_CTX *pctx, int eckdf_nid) | |
125 | { | |
126 | int kdf_nid, kdfmd_nid, cofactor; | |
127 | const EVP_MD *kdf_md; | |
9ab7fe48 | 128 | |
0b3a4ef2 MC |
129 | if (eckdf_nid == NID_undef) |
130 | return 0; | |
131 | ||
132 | /* Lookup KDF type, cofactor mode and digest */ | |
133 | if (!OBJ_find_sigid_algs(eckdf_nid, &kdfmd_nid, &kdf_nid)) | |
134 | return 0; | |
135 | ||
136 | if (kdf_nid == NID_dh_std_kdf) | |
137 | cofactor = 0; | |
138 | else if (kdf_nid == NID_dh_cofactor_kdf) | |
139 | cofactor = 1; | |
140 | else | |
141 | return 0; | |
142 | ||
143 | if (EVP_PKEY_CTX_set_ecdh_cofactor_mode(pctx, cofactor) <= 0) | |
144 | return 0; | |
145 | ||
146 | if (EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_X9_63) <= 0) | |
147 | return 0; | |
148 | ||
149 | kdf_md = EVP_get_digestbynid(kdfmd_nid); | |
150 | if (!kdf_md) | |
151 | return 0; | |
152 | ||
153 | if (EVP_PKEY_CTX_set_ecdh_kdf_md(pctx, kdf_md) <= 0) | |
154 | return 0; | |
155 | return 1; | |
156 | } | |
157 | ||
158 | static int ecdh_cms_set_shared_info(EVP_PKEY_CTX *pctx, CMS_RecipientInfo *ri) | |
159 | { | |
160 | int rv = 0; | |
0b3a4ef2 MC |
161 | X509_ALGOR *alg, *kekalg = NULL; |
162 | ASN1_OCTET_STRING *ukm; | |
163 | const unsigned char *p; | |
164 | unsigned char *der = NULL; | |
165 | int plen, keylen; | |
166 | EVP_CIPHER *kekcipher = NULL; | |
167 | EVP_CIPHER_CTX *kekctx; | |
ad57a13b | 168 | char name[OSSL_MAX_NAME_SIZE]; |
0b3a4ef2 MC |
169 | |
170 | if (!CMS_RecipientInfo_kari_get0_alg(ri, &alg, &ukm)) | |
171 | return 0; | |
172 | ||
173 | if (!ecdh_cms_set_kdf_param(pctx, OBJ_obj2nid(alg->algorithm))) { | |
9311d0c4 | 174 | ERR_raise(ERR_LIB_CMS, CMS_R_KDF_PARAMETER_ERROR); |
0b3a4ef2 MC |
175 | return 0; |
176 | } | |
177 | ||
178 | if (alg->parameter->type != V_ASN1_SEQUENCE) | |
179 | return 0; | |
180 | ||
181 | p = alg->parameter->value.sequence->data; | |
182 | plen = alg->parameter->value.sequence->length; | |
183 | kekalg = d2i_X509_ALGOR(NULL, &p, plen); | |
184 | if (kekalg == NULL) | |
185 | goto err; | |
186 | kekctx = CMS_RecipientInfo_kari_get0_ctx(ri); | |
187 | if (kekctx == NULL) | |
188 | goto err; | |
ad57a13b | 189 | OBJ_obj2txt(name, sizeof(name), kekalg->algorithm, 0); |
0b3a4ef2 | 190 | kekcipher = EVP_CIPHER_fetch(pctx->libctx, name, pctx->propquery); |
ed576acd | 191 | if (kekcipher == NULL || EVP_CIPHER_get_mode(kekcipher) != EVP_CIPH_WRAP_MODE) |
0b3a4ef2 MC |
192 | goto err; |
193 | if (!EVP_EncryptInit_ex(kekctx, kekcipher, NULL, NULL, NULL)) | |
194 | goto err; | |
195 | if (EVP_CIPHER_asn1_to_param(kekctx, kekalg->parameter) <= 0) | |
196 | goto err; | |
197 | ||
ed576acd | 198 | keylen = EVP_CIPHER_CTX_get_key_length(kekctx); |
0b3a4ef2 MC |
199 | if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0) |
200 | goto err; | |
201 | ||
202 | plen = CMS_SharedInfo_encode(&der, kekalg, ukm, keylen); | |
203 | ||
204 | if (plen <= 0) | |
205 | goto err; | |
206 | ||
207 | if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, der, plen) <= 0) | |
208 | goto err; | |
209 | der = NULL; | |
210 | ||
211 | rv = 1; | |
212 | err: | |
213 | EVP_CIPHER_free(kekcipher); | |
214 | X509_ALGOR_free(kekalg); | |
215 | OPENSSL_free(der); | |
216 | return rv; | |
217 | } | |
218 | ||
219 | static int ecdh_cms_decrypt(CMS_RecipientInfo *ri) | |
220 | { | |
221 | EVP_PKEY_CTX *pctx; | |
222 | ||
223 | pctx = CMS_RecipientInfo_get0_pkey_ctx(ri); | |
224 | if (pctx == NULL) | |
225 | return 0; | |
226 | /* See if we need to set peer key */ | |
227 | if (!EVP_PKEY_CTX_get0_peerkey(pctx)) { | |
228 | X509_ALGOR *alg; | |
229 | ASN1_BIT_STRING *pubkey; | |
230 | ||
231 | if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &alg, &pubkey, | |
232 | NULL, NULL, NULL)) | |
233 | return 0; | |
9ab7fe48 | 234 | if (alg == NULL || pubkey == NULL) |
0b3a4ef2 MC |
235 | return 0; |
236 | if (!ecdh_cms_set_peerkey(pctx, alg, pubkey)) { | |
9311d0c4 | 237 | ERR_raise(ERR_LIB_CMS, CMS_R_PEER_KEY_ERROR); |
0b3a4ef2 MC |
238 | return 0; |
239 | } | |
240 | } | |
241 | /* Set ECDH derivation parameters and initialise unwrap context */ | |
242 | if (!ecdh_cms_set_shared_info(pctx, ri)) { | |
9311d0c4 | 243 | ERR_raise(ERR_LIB_CMS, CMS_R_SHARED_INFO_ERROR); |
0b3a4ef2 MC |
244 | return 0; |
245 | } | |
246 | return 1; | |
247 | } | |
248 | ||
249 | static int ecdh_cms_encrypt(CMS_RecipientInfo *ri) | |
250 | { | |
251 | EVP_PKEY_CTX *pctx; | |
252 | EVP_PKEY *pkey; | |
253 | EVP_CIPHER_CTX *ctx; | |
254 | int keylen; | |
255 | X509_ALGOR *talg, *wrap_alg = NULL; | |
256 | const ASN1_OBJECT *aoid; | |
257 | ASN1_BIT_STRING *pubkey; | |
258 | ASN1_STRING *wrap_str; | |
259 | ASN1_OCTET_STRING *ukm; | |
260 | unsigned char *penc = NULL; | |
261 | size_t penclen; | |
262 | int rv = 0; | |
263 | int ecdh_nid, kdf_type, kdf_nid, wrap_nid; | |
264 | const EVP_MD *kdf_md; | |
265 | ||
266 | pctx = CMS_RecipientInfo_get0_pkey_ctx(ri); | |
267 | if (pctx == NULL) | |
268 | return 0; | |
269 | /* Get ephemeral key */ | |
270 | pkey = EVP_PKEY_CTX_get0_pkey(pctx); | |
271 | if (!CMS_RecipientInfo_kari_get0_orig_id(ri, &talg, &pubkey, | |
272 | NULL, NULL, NULL)) | |
273 | goto err; | |
274 | X509_ALGOR_get0(&aoid, NULL, NULL, talg); | |
275 | /* Is everything uninitialised? */ | |
276 | if (aoid == OBJ_nid2obj(NID_undef)) { | |
277 | /* Set the key */ | |
278 | ||
5ac8fb58 | 279 | penclen = EVP_PKEY_get1_encoded_public_key(pkey, &penc); |
0b3a4ef2 | 280 | ASN1_STRING_set0(pubkey, penc, penclen); |
7c310e87 | 281 | ossl_asn1_string_set_bits_left(pubkey, 0); |
0b3a4ef2 MC |
282 | |
283 | penc = NULL; | |
04bc3c12 DDO |
284 | (void)X509_ALGOR_set0(talg, OBJ_nid2obj(NID_X9_62_id_ecPublicKey), |
285 | V_ASN1_UNDEF, NULL); /* cannot fail */ | |
0b3a4ef2 MC |
286 | } |
287 | ||
288 | /* See if custom parameters set */ | |
289 | kdf_type = EVP_PKEY_CTX_get_ecdh_kdf_type(pctx); | |
290 | if (kdf_type <= 0) | |
291 | goto err; | |
2cba2e16 | 292 | if (EVP_PKEY_CTX_get_ecdh_kdf_md(pctx, &kdf_md) <= 0) |
0b3a4ef2 MC |
293 | goto err; |
294 | ecdh_nid = EVP_PKEY_CTX_get_ecdh_cofactor_mode(pctx); | |
295 | if (ecdh_nid < 0) | |
296 | goto err; | |
297 | else if (ecdh_nid == 0) | |
298 | ecdh_nid = NID_dh_std_kdf; | |
299 | else if (ecdh_nid == 1) | |
300 | ecdh_nid = NID_dh_cofactor_kdf; | |
301 | ||
302 | if (kdf_type == EVP_PKEY_ECDH_KDF_NONE) { | |
303 | kdf_type = EVP_PKEY_ECDH_KDF_X9_63; | |
304 | if (EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, kdf_type) <= 0) | |
305 | goto err; | |
306 | } else | |
307 | /* Unknown KDF */ | |
308 | goto err; | |
309 | if (kdf_md == NULL) { | |
310 | /* Fixme later for better MD */ | |
311 | kdf_md = EVP_sha1(); | |
312 | if (EVP_PKEY_CTX_set_ecdh_kdf_md(pctx, kdf_md) <= 0) | |
313 | goto err; | |
314 | } | |
315 | ||
316 | if (!CMS_RecipientInfo_kari_get0_alg(ri, &talg, &ukm)) | |
317 | goto err; | |
318 | ||
319 | /* Lookup NID for KDF+cofactor+digest */ | |
320 | ||
ed576acd | 321 | if (!OBJ_find_sigid_by_algs(&kdf_nid, EVP_MD_get_type(kdf_md), ecdh_nid)) |
0b3a4ef2 MC |
322 | goto err; |
323 | /* Get wrap NID */ | |
324 | ctx = CMS_RecipientInfo_kari_get0_ctx(ri); | |
ed576acd TM |
325 | wrap_nid = EVP_CIPHER_CTX_get_type(ctx); |
326 | keylen = EVP_CIPHER_CTX_get_key_length(ctx); | |
0b3a4ef2 MC |
327 | |
328 | /* Package wrap algorithm in an AlgorithmIdentifier */ | |
329 | ||
330 | wrap_alg = X509_ALGOR_new(); | |
331 | if (wrap_alg == NULL) | |
332 | goto err; | |
333 | wrap_alg->algorithm = OBJ_nid2obj(wrap_nid); | |
334 | wrap_alg->parameter = ASN1_TYPE_new(); | |
335 | if (wrap_alg->parameter == NULL) | |
336 | goto err; | |
337 | if (EVP_CIPHER_param_to_asn1(ctx, wrap_alg->parameter) <= 0) | |
338 | goto err; | |
339 | if (ASN1_TYPE_get(wrap_alg->parameter) == NID_undef) { | |
340 | ASN1_TYPE_free(wrap_alg->parameter); | |
341 | wrap_alg->parameter = NULL; | |
342 | } | |
343 | ||
344 | if (EVP_PKEY_CTX_set_ecdh_kdf_outlen(pctx, keylen) <= 0) | |
345 | goto err; | |
346 | ||
347 | penclen = CMS_SharedInfo_encode(&penc, wrap_alg, ukm, keylen); | |
348 | ||
9ab7fe48 | 349 | if (penclen == 0) |
0b3a4ef2 MC |
350 | goto err; |
351 | ||
352 | if (EVP_PKEY_CTX_set0_ecdh_kdf_ukm(pctx, penc, penclen) <= 0) | |
353 | goto err; | |
354 | penc = NULL; | |
355 | ||
356 | /* | |
357 | * Now need to wrap encoding of wrap AlgorithmIdentifier into parameter | |
358 | * of another AlgorithmIdentifier. | |
359 | */ | |
360 | penclen = i2d_X509_ALGOR(wrap_alg, &penc); | |
9ab7fe48 | 361 | if (penc == NULL || penclen == 0) |
0b3a4ef2 MC |
362 | goto err; |
363 | wrap_str = ASN1_STRING_new(); | |
364 | if (wrap_str == NULL) | |
365 | goto err; | |
366 | ASN1_STRING_set0(wrap_str, penc, penclen); | |
367 | penc = NULL; | |
04bc3c12 DDO |
368 | rv = X509_ALGOR_set0(talg, OBJ_nid2obj(kdf_nid), V_ASN1_SEQUENCE, wrap_str); |
369 | if (!rv) | |
370 | ASN1_STRING_free(wrap_str); | |
0b3a4ef2 MC |
371 | |
372 | err: | |
373 | OPENSSL_free(penc); | |
374 | X509_ALGOR_free(wrap_alg); | |
375 | return rv; | |
376 | } | |
377 | ||
53155f1c | 378 | int ossl_cms_ecdh_envelope(CMS_RecipientInfo *ri, int decrypt) |
0b3a4ef2 | 379 | { |
9ab7fe48 MC |
380 | assert(decrypt == 0 || decrypt == 1); |
381 | ||
0b3a4ef2 MC |
382 | if (decrypt == 1) |
383 | return ecdh_cms_decrypt(ri); | |
9ab7fe48 MC |
384 | |
385 | if (decrypt == 0) | |
0b3a4ef2 MC |
386 | return ecdh_cms_encrypt(ri); |
387 | ||
9311d0c4 | 388 | ERR_raise(ERR_LIB_CMS, CMS_R_NOT_SUPPORTED_FOR_THIS_KEY_TYPE); |
0b3a4ef2 | 389 | return 0; |
9ab7fe48 | 390 | } |