Allow KDC to always return the salt in the PA-ETYPE-INFO[2]
[heimdal.git] / kdc / pkinit-ec.c
blobc718aa79962bbd6a053161df8962d50a115db443
1 /*
2 * Copyright (c) 2016 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
36 #include <config.h>
37 #include <roken.h>
39 #ifdef PKINIT
42 * As with the other *-ec.c files in Heimdal, this is a bit of a hack.
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.
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.
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 */
62 #define NO_HCRYPTO_POLLUTION
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>
71 #include <hx509.h>
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)
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);
84 #endif
86 void
87 _kdc_pk_free_client_ec_param(krb5_context context,
88 void *ec_key_pk,
89 void *ec_key_key)
91 #ifdef HAVE_HCRYPTO_W_OPENSSL
92 free_client_ec_param(context, ec_key_pk, ec_key_key);
93 #endif
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)
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;
112 *dh_gen_key = NULL;
113 *dh_gen_keylen = 0;
114 *ec_key_key = NULL;
116 memset(&key, 0, sizeof(key));
118 if (ec_key_pk == NULL) {
119 ret = KRB5KRB_ERR_GENERIC;
120 krb5_set_error_message(context, ret, "public_key");
121 return ret;
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;
132 ephemeral = EC_KEY_new();
133 if (ephemeral == NULL)
134 return krb5_enomem(context);
136 EC_KEY_set_group(ephemeral, group);
138 if (EC_KEY_generate_key(ephemeral) != 1) {
139 EC_KEY_free(ephemeral);
140 return krb5_enomem(context);
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);
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;
162 *ec_key_key = ephemeral;
163 *dh_gen_key = p;
164 *dh_gen_keylen = len;
166 return 0;
168 #endif /* HAVE_HCRYPTO_W_OPENSSL */
170 krb5_error_code
171 _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)
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 return ENOTSUP;
183 #endif /* HAVE_HCRYPTO_W_OPENSSL */
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)
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;
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;
207 memset(&ecp, 0, sizeof(ecp));
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;
214 if (ecp.element != choice_ECParameters_namedCurve) {
215 ret = KRB5_BADMSGTYPE;
216 goto out;
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;
226 /* XXX verify group is ok */
228 public = EC_KEY_new_by_curve_name(nid);
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;
238 *out = public;
239 public = NULL;
241 out:
242 if (public)
243 EC_KEY_free(public);
244 free_ECParameters(&ecp);
245 return ret;
247 #endif /* HAVE_HCRYPTO_W_OPENSSL */
249 krb5_error_code
250 _kdc_get_ecdh_param(krb5_context context,
251 krb5_kdc_configuration *config,
252 SubjectPublicKeyInfo *dh_key_info,
253 void **out)
255 #ifdef HAVE_HCRYPTO_W_OPENSSL
256 return get_ecdh_param(context, config, dh_key_info, (EC_KEY **)out);
257 #else
258 return ENOTSUP;
259 #endif /* HAVE_HCRYPTO_W_OPENSSL */
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)
274 krb5_error_code ret = 0;
275 unsigned char *p;
276 int len;
278 *out = NULL;
279 *out_len = 0;
281 len = i2o_ECPublicKey(key, NULL);
282 if (len <= 0)
283 return EOVERFLOW;
285 *out = malloc(len);
286 if (*out == NULL)
287 return krb5_enomem(context);
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;
300 *out_len = len * 8;
301 return ret;
303 #endif
305 krb5_error_code
306 _kdc_serialize_ecdh_key(krb5_context context,
307 void *key,
308 unsigned char **out,
309 size_t *out_len)
311 #ifdef HAVE_HCRYPTO_W_OPENSSL
312 return serialize_ecdh_key(context, key, out, out_len);
313 #else
314 return ENOTSUP;
315 #endif
318 #endif