/src/samba/third_party/heimdal/lib/krb5/pkinit-ec.c
Line | Count | Source |
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 | | _krb5_build_authpack_subjectPK_EC(krb5_context context, |
89 | | krb5_pk_init_ctx ctx, |
90 | | AuthPack *a) |
91 | 0 | { |
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 | 0 | #endif |
176 | 0 | } |
177 | | |
178 | | krb5_error_code |
179 | | _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 | 0 | { |
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 | 0 | #endif |
296 | 0 | } |
297 | | |
298 | | void |
299 | | _krb5_pk_eckey_free(void *eckey) |
300 | 0 | { |
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 |