Line data Source code
1 : /*
2 : * Copyright (c) 2016 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : *
19 : * 3. Neither the name of the Institute nor the names of its contributors
20 : * may be used to endorse or promote products derived from this software
21 : * without specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 : * SUCH DAMAGE.
34 : */
35 :
36 : #include <config.h>
37 : #include <roken.h>
38 :
39 : #ifdef PKINIT
40 :
41 : /*
42 : * As with the other *-ec.c files in Heimdal, this is a bit of a hack.
43 : *
44 : * The idea is to use OpenSSL for EC because hcrypto doesn't have the
45 : * required functionality at this time. To do this we segregate
46 : * EC-using code into separate source files and then we arrange for them
47 : * to get the OpenSSL headers and not the conflicting hcrypto ones.
48 : *
49 : * Because of auto-generated *-private.h headers, we end up needing to
50 : * make sure various types are defined before we include them, thus the
51 : * strange header include order here.
52 : */
53 :
54 : #ifdef HAVE_HCRYPTO_W_OPENSSL
55 : #include <openssl/ec.h>
56 : #include <openssl/ecdh.h>
57 : #include <openssl/evp.h>
58 : #include <openssl/bn.h>
59 : #include <openssl/dh.h>
60 : #define HEIM_NO_CRYPTO_HDRS
61 : #endif
62 :
63 : /*
64 : * NO_HCRYPTO_POLLUTION -> don't refer to hcrypto type/function names
65 : * that we don't need in this file and which would clash with OpenSSL's
66 : * in ways that are difficult to address in cleaner ways.
67 : *
68 : * In the medium- to long-term what we should do is move all PK in
69 : * Heimdal to the newer EVP interfaces for PK and then nothing outside
70 : * lib/hcrypto should ever have to include OpenSSL headers, and -more
71 : * specifically- the only thing that should ever have to include OpenSSL
72 : * headers is the OpenSSL backend to hcrypto.
73 : */
74 : #define NO_HCRYPTO_POLLUTION
75 :
76 : #include "krb5_locl.h"
77 : #include <hcrypto/des.h>
78 : #include <cms_asn1.h>
79 : #include <pkcs8_asn1.h>
80 : #include <pkcs9_asn1.h>
81 : #include <pkcs12_asn1.h>
82 : #include <pkinit_asn1.h>
83 : #include <asn1_err.h>
84 :
85 : #include <der.h>
86 :
87 : krb5_error_code
88 0 : _krb5_build_authpack_subjectPK_EC(krb5_context context,
89 : krb5_pk_init_ctx ctx,
90 : AuthPack *a)
91 : {
92 : #ifdef HAVE_HCRYPTO_W_OPENSSL
93 : krb5_error_code ret;
94 : ECParameters ecp;
95 : unsigned char *p;
96 : size_t size;
97 : int xlen;
98 :
99 : /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
100 :
101 : ecp.element = choice_ECParameters_namedCurve;
102 : ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
103 : &ecp.u.namedCurve);
104 : if (ret)
105 : return ret;
106 :
107 : ALLOC(a->clientPublicValue->algorithm.parameters, 1);
108 : if (a->clientPublicValue->algorithm.parameters == NULL) {
109 : free_ECParameters(&ecp);
110 : return krb5_enomem(context);
111 : }
112 : ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
113 : free_ECParameters(&ecp);
114 : if (ret)
115 : return ret;
116 : if ((int)size != xlen)
117 : krb5_abortx(context, "asn1 internal error");
118 :
119 : a->clientPublicValue->algorithm.parameters->data = p;
120 : a->clientPublicValue->algorithm.parameters->length = size;
121 :
122 : /* copy in public key */
123 :
124 : ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
125 : &a->clientPublicValue->algorithm.algorithm);
126 : if (ret)
127 : return ret;
128 :
129 : #ifdef HAVE_OPENSSL_30
130 : ctx->u.eckey = EVP_EC_gen(OSSL_EC_curve_nid2name(NID_X9_62_prime256v1));
131 : #else
132 : ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
133 : if (ctx->u.eckey == NULL)
134 : return krb5_enomem(context);
135 :
136 : ret = EC_KEY_generate_key(ctx->u.eckey);
137 : if (ret != 1)
138 : return EINVAL;
139 : #endif
140 :
141 : #ifdef HAVE_OPENSSL_30
142 : xlen = i2d_PublicKey(ctx->u.eckey, NULL);
143 : #else
144 : xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
145 : #endif
146 : if (xlen <= 0)
147 : return EINVAL;
148 :
149 : p = malloc(xlen);
150 : if (p == NULL)
151 : return krb5_enomem(context);
152 :
153 : a->clientPublicValue->subjectPublicKey.data = p;
154 :
155 : #ifdef HAVE_OPENSSL_30
156 : xlen = i2d_PublicKey(ctx->u.eckey, &p);
157 : #else
158 : xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
159 : #endif
160 : if (xlen <= 0) {
161 : a->clientPublicValue->subjectPublicKey.data = NULL;
162 : free(p);
163 : return EINVAL;
164 : }
165 :
166 : a->clientPublicValue->subjectPublicKey.length = xlen * 8;
167 :
168 : return 0;
169 :
170 : /* XXX verify that this is right with RFC3279 */
171 : #else
172 0 : krb5_set_error_message(context, ENOTSUP,
173 0 : N_("PKINIT: ECDH not supported", ""));
174 0 : return ENOTSUP;
175 : #endif
176 : }
177 :
178 : krb5_error_code
179 0 : _krb5_pk_rd_pa_reply_ecdh_compute_key(krb5_context context,
180 : krb5_pk_init_ctx ctx,
181 : const unsigned char *in,
182 : size_t in_sz,
183 : unsigned char **out,
184 : int *out_sz)
185 : {
186 : #ifdef HAVE_HCRYPTO_W_OPENSSL
187 : #ifdef HAVE_OPENSSL_30
188 : krb5_error_code ret = 0;
189 : EVP_PKEY_CTX *pctx = NULL;
190 : EVP_PKEY *template = NULL;
191 : EVP_PKEY *public = NULL;
192 : size_t shared_len = 0;
193 :
194 : if ((template = EVP_PKEY_new()) == NULL)
195 : ret = krb5_enomem(context);
196 : if (ret == 0 &&
197 : EVP_PKEY_copy_parameters(template, ctx->u.eckey) != 1)
198 : ret = krb5_enomem(context);
199 : if (ret == 0 && (pctx = EVP_PKEY_CTX_new(ctx->u.eckey, NULL)) == NULL)
200 : ret = krb5_enomem(context);
201 : if (ret == 0 && EVP_PKEY_derive_init(pctx) != 1)
202 : ret = krb5_enomem(context);
203 : if (ret == 0 &&
204 : EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_NONE) != 1)
205 : ret = krb5_enomem(context);
206 : if (ret == 0 &&
207 : (public = d2i_PublicKey(EVP_PKEY_EC, &template, &in, in_sz)) == NULL)
208 : krb5_set_error_message(context,
209 : ret = HX509_PARSING_KEY_FAILED,
210 : "PKINIT: Can't parse the KDC's ECDH public key");
211 : if (ret == 0 &&
212 : EVP_PKEY_derive_set_peer_ex(pctx, public, 1) != 1)
213 : krb5_set_error_message(context,
214 : ret = KRB5KRB_ERR_GENERIC,
215 : "Could not derive ECDH shared secret for PKINIT key exchange "
216 : "(EVP_PKEY_derive_set_peer_ex)");
217 : if (ret == 0 &&
218 : (EVP_PKEY_derive(pctx, NULL, &shared_len) != 1 || shared_len == 0))
219 : krb5_set_error_message(context,
220 : ret = KRB5KRB_ERR_GENERIC,
221 : "Could not derive ECDH shared secret for PKINIT key exchange "
222 : "(EVP_PKEY_derive to get length)");
223 : if (ret == 0 && shared_len > INT_MAX)
224 : krb5_set_error_message(context,
225 : ret = KRB5KRB_ERR_GENERIC,
226 : "Could not derive ECDH shared secret for PKINIT key exchange "
227 : "(shared key too large)");
228 : if (ret == 0 && (*out = malloc(shared_len)) == NULL)
229 : ret = krb5_enomem(context);
230 : if (ret == 0 && EVP_PKEY_derive(pctx, *out, &shared_len) != 1)
231 : krb5_set_error_message(context,
232 : ret = KRB5KRB_ERR_GENERIC,
233 : "Could not derive ECDH shared secret for PKINIT key exchange "
234 : "(EVP_PKEY_derive)");
235 : if (ret == 0)
236 : *out_sz = shared_len;
237 : EVP_PKEY_CTX_free(pctx); // move
238 : EVP_PKEY_free(template);
239 :
240 : return ret;
241 : #else
242 : krb5_error_code ret = 0;
243 : int dh_gen_keylen;
244 :
245 : const EC_GROUP *group;
246 : EC_KEY *public = NULL;
247 :
248 : group = EC_KEY_get0_group(ctx->u.eckey);
249 :
250 : public = EC_KEY_new();
251 : if (public == NULL)
252 : return krb5_enomem(context);
253 : if (EC_KEY_set_group(public, group) != 1) {
254 : EC_KEY_free(public);
255 : return krb5_enomem(context);
256 : }
257 :
258 : if (o2i_ECPublicKey(&public, &in, in_sz) == NULL) {
259 : EC_KEY_free(public);
260 : ret = KRB5KRB_ERR_GENERIC;
261 : krb5_set_error_message(context, ret,
262 : N_("PKINIT: Can't parse ECDH public key", ""));
263 : return ret;
264 : }
265 :
266 : *out_sz = (EC_GROUP_get_degree(group) + 7) / 8;
267 : if (*out_sz < 0)
268 : return EOVERFLOW;
269 : *out = malloc(*out_sz);
270 : if (*out == NULL) {
271 : EC_KEY_free(public);
272 : return krb5_enomem(context);
273 : }
274 : dh_gen_keylen = ECDH_compute_key(*out, *out_sz,
275 : EC_KEY_get0_public_key(public),
276 : ctx->u.eckey, NULL);
277 : EC_KEY_free(public);
278 : if (dh_gen_keylen <= 0) {
279 : ret = KRB5KRB_ERR_GENERIC;
280 : dh_gen_keylen = 0;
281 : krb5_set_error_message(context, ret,
282 : N_("PKINIT: Can't compute ECDH public key", ""));
283 : free(*out);
284 : *out = NULL;
285 : *out_sz = 0;
286 : }
287 : *out_sz = dh_gen_keylen;
288 :
289 : return ret;
290 : #endif
291 : #else
292 0 : krb5_set_error_message(context, ENOTSUP,
293 0 : N_("PKINIT: ECDH not supported", ""));
294 0 : return ENOTSUP;
295 : #endif
296 : }
297 :
298 : void
299 0 : _krb5_pk_eckey_free(void *eckey)
300 : {
301 : #ifdef HAVE_HCRYPTO_W_OPENSSL
302 : #ifdef HAVE_OPENSSL_30
303 : EVP_PKEY_free(eckey);
304 : #else
305 : EC_KEY_free(eckey);
306 : #endif
307 : #endif
308 0 : }
309 :
310 : #else
311 :
312 : static char lib_krb5_pkinit_ec_c = '\0';
313 :
314 : #endif
|