2 * Copyright (c) 2016 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
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
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
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
65 #include <hcrypto/des.h>
66 #include <heim_asn1.h>
67 #include <rfc2459_asn1.h>
69 #include <pkinit_asn1.h>
73 #ifdef HAVE_HCRYPTO_W_OPENSSL
75 free_client_ec_param(krb5_context context
,
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
);
87 _kdc_pk_free_client_ec_param(krb5_context context
,
91 #ifdef HAVE_HCRYPTO_W_OPENSSL
92 free_client_ec_param(context
, ec_key_pk
, ec_key_key
);
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
;
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");
124 group
= EC_KEY_get0_group(ec_key_pk
);
126 ret
= KRB5KRB_ERR_GENERIC
;
127 krb5_set_error_message(context
, ret
, "failed to get the group of "
128 "the client's public key");
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;
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
),
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");
162 *ec_key_key
= ephemeral
;
164 *dh_gen_keylen
= len
;
168 #endif /* HAVE_HCRYPTO_W_OPENSSL */
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
);
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
,
194 EC_KEY
*public = NULL
;
196 const unsigned char *p
;
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
);
214 if (ecp
.element
!= choice_ECParameters_namedCurve
) {
215 ret
= KRB5_BADMSGTYPE
;
219 if (der_heim_oid_cmp(&ecp
.u
.namedCurve
, &asn1_oid_id_ec_group_secp256r1
) == 0)
220 nid
= NID_X9_62_prime256v1
;
222 ret
= KRB5_BADMSGTYPE
;
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");
244 free_ECParameters(&ecp
);
247 #endif /* HAVE_HCRYPTO_W_OPENSSL */
250 _kdc_get_ecdh_param(krb5_context context
,
251 krb5_kdc_configuration
*config
,
252 SubjectPublicKeyInfo
*dh_key_info
,
255 #ifdef HAVE_HCRYPTO_W_OPENSSL
256 return get_ecdh_param(context
, config
, dh_key_info
, (EC_KEY
**)out
);
259 #endif /* HAVE_HCRYPTO_W_OPENSSL */
267 #ifdef HAVE_HCRYPTO_W_OPENSSL
268 static krb5_error_code
269 serialize_ecdh_key(krb5_context context
,
274 krb5_error_code ret
= 0;
281 len
= i2o_ECPublicKey(key
, NULL
);
287 return krb5_enomem(context
);
290 len
= i2o_ECPublicKey(key
, &p
);
294 ret
= EINVAL
; /* XXX Better error please */
295 krb5_set_error_message(context
, ret
,
296 "PKINIT failed to encode ECDH key");
306 _kdc_serialize_ecdh_key(krb5_context context
,
311 #ifdef HAVE_HCRYPTO_W_OPENSSL
312 return serialize_ecdh_key(context
, key
, out
, out_len
);