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