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