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 : #define HEIM_NO_CRYPTO_HDRS
60 : #endif /* HAVE_HCRYPTO_W_OPENSSL */
61 :
62 : #define NO_HCRYPTO_POLLUTION
63 :
64 : #include "kdc_locl.h"
65 : #include <hcrypto/des.h>
66 : #include <heim_asn1.h>
67 : #include <rfc2459_asn1.h>
68 : #include <cms_asn1.h>
69 : #include <pkinit_asn1.h>
70 :
71 : #include <hx509.h>
72 :
73 : #ifdef HAVE_HCRYPTO_W_OPENSSL
74 : static void
75 : free_client_ec_param(krb5_context context,
76 : EC_KEY *ec_key_pk,
77 : EC_KEY *ec_key_key)
78 : {
79 : if (ec_key_pk != NULL)
80 : EC_KEY_free(ec_key_pk);
81 : if (ec_key_key != NULL)
82 : EC_KEY_free(ec_key_key);
83 : }
84 : #endif
85 :
86 : void
87 0 : _kdc_pk_free_client_ec_param(krb5_context context,
88 : void *ec_key_pk,
89 : void *ec_key_key)
90 : {
91 : #ifdef HAVE_HCRYPTO_W_OPENSSL
92 : free_client_ec_param(context, ec_key_pk, ec_key_key);
93 : #endif
94 0 : }
95 :
96 : #ifdef HAVE_HCRYPTO_W_OPENSSL
97 : static krb5_error_code
98 : generate_ecdh_keyblock(krb5_context context,
99 : EC_KEY *ec_key_pk, /* the client's public key */
100 : EC_KEY **ec_key_key, /* the KDC's ephemeral private */
101 : unsigned char **dh_gen_key, /* shared secret */
102 : size_t *dh_gen_keylen)
103 : {
104 : const EC_GROUP *group;
105 : EC_KEY *ephemeral;
106 : krb5_keyblock key;
107 : krb5_error_code ret;
108 : unsigned char *p;
109 : size_t size;
110 : int len;
111 :
112 : *dh_gen_key = NULL;
113 : *dh_gen_keylen = 0;
114 : *ec_key_key = NULL;
115 :
116 : memset(&key, 0, sizeof(key));
117 :
118 : if (ec_key_pk == NULL) {
119 : ret = KRB5KRB_ERR_GENERIC;
120 : krb5_set_error_message(context, ret, "public_key");
121 : return ret;
122 : }
123 :
124 : group = EC_KEY_get0_group(ec_key_pk);
125 : if (group == NULL) {
126 : ret = KRB5KRB_ERR_GENERIC;
127 : krb5_set_error_message(context, ret, "failed to get the group of "
128 : "the client's public key");
129 : return ret;
130 : }
131 :
132 : ephemeral = EC_KEY_new();
133 : if (ephemeral == NULL)
134 : return krb5_enomem(context);
135 :
136 : EC_KEY_set_group(ephemeral, group);
137 :
138 : if (EC_KEY_generate_key(ephemeral) != 1) {
139 : EC_KEY_free(ephemeral);
140 : return krb5_enomem(context);
141 : }
142 :
143 : size = (EC_GROUP_get_degree(group) + 7) / 8;
144 : p = malloc(size);
145 : if (p == NULL) {
146 : EC_KEY_free(ephemeral);
147 : return krb5_enomem(context);
148 : }
149 :
150 : len = ECDH_compute_key(p, size,
151 : EC_KEY_get0_public_key(ec_key_pk),
152 : ephemeral, NULL);
153 : if (len <= 0) {
154 : free(p);
155 : EC_KEY_free(ephemeral);
156 : ret = KRB5KRB_ERR_GENERIC;
157 : krb5_set_error_message(context, ret, "Failed to compute ECDH "
158 : "public shared secret");
159 : return ret;
160 : }
161 :
162 : *ec_key_key = ephemeral;
163 : *dh_gen_key = p;
164 : *dh_gen_keylen = len;
165 :
166 : return 0;
167 : }
168 : #endif /* HAVE_HCRYPTO_W_OPENSSL */
169 :
170 : krb5_error_code
171 0 : _kdc_generate_ecdh_keyblock(krb5_context context,
172 : void *ec_key_pk, /* the client's public key */
173 : void **ec_key_key, /* the KDC's ephemeral private */
174 : unsigned char **dh_gen_key, /* shared secret */
175 : size_t *dh_gen_keylen)
176 : {
177 : #ifdef HAVE_HCRYPTO_W_OPENSSL
178 : return generate_ecdh_keyblock(context, ec_key_pk,
179 : (EC_KEY **)ec_key_key,
180 : dh_gen_key, dh_gen_keylen);
181 : #else
182 0 : return ENOTSUP;
183 : #endif /* HAVE_HCRYPTO_W_OPENSSL */
184 : }
185 :
186 : #ifdef HAVE_HCRYPTO_W_OPENSSL
187 : static krb5_error_code
188 : get_ecdh_param(krb5_context context,
189 : krb5_kdc_configuration *config,
190 : SubjectPublicKeyInfo *dh_key_info,
191 : EC_KEY **out)
192 : {
193 : ECParameters ecp;
194 : EC_KEY *public = NULL;
195 : krb5_error_code ret;
196 : const unsigned char *p;
197 : size_t len;
198 : int nid;
199 :
200 : if (dh_key_info->algorithm.parameters == NULL) {
201 : krb5_set_error_message(context, KRB5_BADMSGTYPE,
202 : "PKINIT missing algorithm parameter "
203 : "in clientPublicValue");
204 : return KRB5_BADMSGTYPE;
205 : }
206 :
207 : memset(&ecp, 0, sizeof(ecp));
208 :
209 : ret = decode_ECParameters(dh_key_info->algorithm.parameters->data,
210 : dh_key_info->algorithm.parameters->length, &ecp, &len);
211 : if (ret)
212 : goto out;
213 :
214 : if (ecp.element != choice_ECParameters_namedCurve) {
215 : ret = KRB5_BADMSGTYPE;
216 : goto out;
217 : }
218 :
219 : if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0)
220 : nid = NID_X9_62_prime256v1;
221 : else {
222 : ret = KRB5_BADMSGTYPE;
223 : goto out;
224 : }
225 :
226 : /* XXX verify group is ok */
227 :
228 : public = EC_KEY_new_by_curve_name(nid);
229 :
230 : p = dh_key_info->subjectPublicKey.data;
231 : len = dh_key_info->subjectPublicKey.length / 8;
232 : if (o2i_ECPublicKey(&public, &p, len) == NULL) {
233 : ret = KRB5_BADMSGTYPE;
234 : krb5_set_error_message(context, ret,
235 : "PKINIT failed to decode ECDH key");
236 : goto out;
237 : }
238 : *out = public;
239 : public = NULL;
240 :
241 : out:
242 : if (public)
243 : EC_KEY_free(public);
244 : free_ECParameters(&ecp);
245 : return ret;
246 : }
247 : #endif /* HAVE_HCRYPTO_W_OPENSSL */
248 :
249 : krb5_error_code
250 0 : _kdc_get_ecdh_param(krb5_context context,
251 : krb5_kdc_configuration *config,
252 : SubjectPublicKeyInfo *dh_key_info,
253 : void **out)
254 : {
255 : #ifdef HAVE_HCRYPTO_W_OPENSSL
256 : return get_ecdh_param(context, config, dh_key_info, (EC_KEY **)out);
257 : #else
258 0 : return ENOTSUP;
259 : #endif /* HAVE_HCRYPTO_W_OPENSSL */
260 : }
261 :
262 :
263 : /*
264 : *
265 : */
266 :
267 : #ifdef HAVE_HCRYPTO_W_OPENSSL
268 : static krb5_error_code
269 : serialize_ecdh_key(krb5_context context,
270 : EC_KEY *key,
271 : unsigned char **out,
272 : size_t *out_len)
273 : {
274 : krb5_error_code ret = 0;
275 : unsigned char *p;
276 : int len;
277 :
278 : *out = NULL;
279 : *out_len = 0;
280 :
281 : len = i2o_ECPublicKey(key, NULL);
282 : if (len <= 0)
283 : return EOVERFLOW;
284 :
285 : *out = malloc(len);
286 : if (*out == NULL)
287 : return krb5_enomem(context);
288 :
289 : p = *out;
290 : len = i2o_ECPublicKey(key, &p);
291 : if (len <= 0) {
292 : free(*out);
293 : *out = NULL;
294 : ret = EINVAL; /* XXX Better error please */
295 : krb5_set_error_message(context, ret,
296 : "PKINIT failed to encode ECDH key");
297 : return ret;
298 : }
299 :
300 : *out_len = len * 8;
301 : return ret;
302 : }
303 : #endif
304 :
305 : krb5_error_code
306 0 : _kdc_serialize_ecdh_key(krb5_context context,
307 : void *key,
308 : unsigned char **out,
309 : size_t *out_len)
310 : {
311 : #ifdef HAVE_HCRYPTO_W_OPENSSL
312 : return serialize_ecdh_key(context, key, out, out_len);
313 : #else
314 0 : return ENOTSUP;
315 : #endif
316 : }
317 :
318 : #endif
|