]>
Commit | Line | Data |
---|---|---|
ea0823df | 1 | /* |
4a6f97d0 | 2 | * Copyright (C) 2008-2016 Tobias Brunner |
e35c3e2a | 3 | * Copyright (C) 2009 Martin Willi |
4a6f97d0 | 4 | * HSR Hochschule fuer Technik Rapperswil |
ea0823df TB |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2 of the License, or (at your | |
9 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
13 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | * for more details. | |
ea0823df TB |
15 | */ |
16 | ||
5a367e99 TB |
17 | #include <openssl/opensslconf.h> |
18 | ||
a3a190b7 | 19 | #ifndef OPENSSL_NO_ECDSA |
5a367e99 | 20 | |
ea0823df TB |
21 | #include "openssl_ec_private_key.h" |
22 | #include "openssl_ec_public_key.h" | |
23 | #include "openssl_util.h" | |
24 | ||
f05b4272 | 25 | #include <utils/debug.h> |
ea0823df TB |
26 | |
27 | #include <openssl/evp.h> | |
28 | #include <openssl/ecdsa.h> | |
e35c3e2a | 29 | #include <openssl/x509.h> |
ea0823df | 30 | |
6688f798 TB |
31 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
32 | OPENSSL_KEY_FALLBACK(ECDSA_SIG, r, s) | |
33 | #endif | |
34 | ||
ea0823df TB |
35 | typedef struct private_openssl_ec_private_key_t private_openssl_ec_private_key_t; |
36 | ||
37 | /** | |
38 | * Private data of a openssl_ec_private_key_t object. | |
39 | */ | |
40 | struct private_openssl_ec_private_key_t { | |
41 | /** | |
42 | * Public interface for this signer. | |
43 | */ | |
44 | openssl_ec_private_key_t public; | |
7daf5226 | 45 | |
ea0823df TB |
46 | /** |
47 | * EC key object | |
48 | */ | |
49 | EC_KEY *ec; | |
7daf5226 | 50 | |
b2266280 TB |
51 | /** |
52 | * TRUE if the key is from an OpenSSL ENGINE and might not be readable | |
53 | */ | |
54 | bool engine; | |
55 | ||
ea0823df TB |
56 | /** |
57 | * reference count | |
58 | */ | |
472cb4ce | 59 | refcount_t ref; |
ea0823df TB |
60 | }; |
61 | ||
b12c6d16 | 62 | /* from ec public key */ |
da9724e6 | 63 | bool openssl_ec_fingerprint(EC_KEY *ec, cred_encoding_type_t type, chunk_t *fp); |
b12c6d16 | 64 | |
ea0823df | 65 | /** |
472cb4ce | 66 | * Build a signature as in RFC 4754 |
ea0823df TB |
67 | */ |
68 | static bool build_signature(private_openssl_ec_private_key_t *this, | |
11e6d285 | 69 | chunk_t hash, chunk_t *signature) |
ea0823df | 70 | { |
6688f798 | 71 | const BIGNUM *r, *s; |
e35c3e2a | 72 | ECDSA_SIG *sig; |
6688f798 | 73 | bool built = FALSE; |
7daf5226 | 74 | |
e35c3e2a | 75 | sig = ECDSA_do_sign(hash.ptr, hash.len, this->ec); |
472cb4ce | 76 | if (sig) |
ea0823df | 77 | { |
6688f798 | 78 | ECDSA_SIG_get0(sig, &r, &s); |
472cb4ce MW |
79 | /* concatenate BNs r/s to a signature chunk */ |
80 | built = openssl_bn_cat(EC_FIELD_ELEMENT_LEN(EC_KEY_get0_group(this->ec)), | |
6688f798 | 81 | r, s, signature); |
472cb4ce | 82 | ECDSA_SIG_free(sig); |
ea0823df | 83 | } |
472cb4ce | 84 | return built; |
ea0823df TB |
85 | } |
86 | ||
87 | /** | |
472cb4ce | 88 | * Build a RFC 4754 signature for a specified curve and hash algorithm |
ea0823df | 89 | */ |
472cb4ce MW |
90 | static bool build_curve_signature(private_openssl_ec_private_key_t *this, |
91 | signature_scheme_t scheme, int nid_hash, | |
92 | int nid_curve, chunk_t data, chunk_t *signature) | |
ea0823df | 93 | { |
472cb4ce MW |
94 | const EC_GROUP *my_group; |
95 | EC_GROUP *req_group; | |
96 | chunk_t hash; | |
97 | bool built; | |
7daf5226 | 98 | |
472cb4ce MW |
99 | req_group = EC_GROUP_new_by_curve_name(nid_curve); |
100 | if (!req_group) | |
101 | { | |
8b0e0910 | 102 | DBG1(DBG_LIB, "signature scheme %N not supported in EC (required curve " |
472cb4ce MW |
103 | "not supported)", signature_scheme_names, scheme); |
104 | return FALSE; | |
105 | } | |
106 | my_group = EC_KEY_get0_group(this->ec); | |
107 | if (EC_GROUP_cmp(my_group, req_group, NULL) != 0) | |
108 | { | |
8b0e0910 | 109 | DBG1(DBG_LIB, "signature scheme %N not supported by private key", |
472cb4ce MW |
110 | signature_scheme_names, scheme); |
111 | return FALSE; | |
112 | } | |
113 | EC_GROUP_free(req_group); | |
114 | if (!openssl_hash_chunk(nid_hash, data, &hash)) | |
115 | { | |
116 | return FALSE; | |
117 | } | |
e201f53e | 118 | built = build_signature(this, hash, signature); |
472cb4ce MW |
119 | chunk_free(&hash); |
120 | return built; | |
ea0823df TB |
121 | } |
122 | ||
123 | /** | |
472cb4ce | 124 | * Build a DER encoded signature as in RFC 3279 |
ea0823df | 125 | */ |
472cb4ce MW |
126 | static bool build_der_signature(private_openssl_ec_private_key_t *this, |
127 | int hash_nid, chunk_t data, chunk_t *signature) | |
ea0823df | 128 | { |
472cb4ce MW |
129 | chunk_t hash, sig; |
130 | int siglen = 0; | |
131 | bool built; | |
7daf5226 | 132 | |
472cb4ce MW |
133 | if (!openssl_hash_chunk(hash_nid, data, &hash)) |
134 | { | |
135 | return FALSE; | |
136 | } | |
137 | sig = chunk_alloc(ECDSA_size(this->ec)); | |
138 | built = ECDSA_sign(0, hash.ptr, hash.len, sig.ptr, &siglen, this->ec) == 1; | |
139 | sig.len = siglen; | |
140 | if (built) | |
ea0823df | 141 | { |
472cb4ce | 142 | *signature = sig; |
ea0823df | 143 | } |
11e6d285 | 144 | else |
ea0823df | 145 | { |
472cb4ce MW |
146 | free(sig.ptr); |
147 | } | |
148 | free(hash.ptr); | |
149 | return built; | |
150 | } | |
151 | ||
57202484 MW |
152 | METHOD(private_key_t, sign, bool, |
153 | private_openssl_ec_private_key_t *this, signature_scheme_t scheme, | |
de280c2e | 154 | void *params, chunk_t data, chunk_t *signature) |
472cb4ce MW |
155 | { |
156 | switch (scheme) | |
157 | { | |
158 | case SIGN_ECDSA_WITH_NULL: | |
159 | return build_signature(this, data, signature); | |
160 | case SIGN_ECDSA_WITH_SHA1_DER: | |
161 | return build_der_signature(this, NID_sha1, data, signature); | |
162 | case SIGN_ECDSA_WITH_SHA256_DER: | |
163 | return build_der_signature(this, NID_sha256, data, signature); | |
164 | case SIGN_ECDSA_WITH_SHA384_DER: | |
165 | return build_der_signature(this, NID_sha384, data, signature); | |
166 | case SIGN_ECDSA_WITH_SHA512_DER: | |
167 | return build_der_signature(this, NID_sha512, data, signature); | |
168 | case SIGN_ECDSA_256: | |
7daf5226 | 169 | return build_curve_signature(this, scheme, NID_sha256, |
1dbaec21 | 170 | NID_X9_62_prime256v1, data, signature); |
472cb4ce | 171 | case SIGN_ECDSA_384: |
1dbaec21 AS |
172 | return build_curve_signature(this, scheme, NID_sha384, |
173 | NID_secp384r1, data, signature); | |
472cb4ce | 174 | case SIGN_ECDSA_521: |
1dbaec21 AS |
175 | return build_curve_signature(this, scheme, NID_sha512, |
176 | NID_secp521r1, data, signature); | |
472cb4ce | 177 | default: |
8b0e0910 | 178 | DBG1(DBG_LIB, "signature scheme %N not supported", |
472cb4ce | 179 | signature_scheme_names, scheme); |
11e6d285 | 180 | return FALSE; |
472cb4ce | 181 | } |
ea0823df TB |
182 | } |
183 | ||
57202484 | 184 | METHOD(private_key_t, decrypt, bool, |
33ddaaab | 185 | private_openssl_ec_private_key_t *this, encryption_scheme_t scheme, |
4abb29f6 | 186 | void *params, chunk_t crypto, chunk_t *plain) |
ea0823df | 187 | { |
8b0e0910 | 188 | DBG1(DBG_LIB, "EC private key decryption not implemented"); |
ea0823df TB |
189 | return FALSE; |
190 | } | |
191 | ||
a944d209 | 192 | METHOD(private_key_t, get_keysize, int, |
57202484 | 193 | private_openssl_ec_private_key_t *this) |
ea0823df | 194 | { |
58184676 | 195 | return EC_GROUP_get_degree(EC_KEY_get0_group(this->ec)); |
ea0823df TB |
196 | } |
197 | ||
57202484 MW |
198 | METHOD(private_key_t, get_type, key_type_t, |
199 | private_openssl_ec_private_key_t *this) | |
472cb4ce MW |
200 | { |
201 | return KEY_ECDSA; | |
202 | } | |
203 | ||
57202484 MW |
204 | METHOD(private_key_t, get_public_key, public_key_t*, |
205 | private_openssl_ec_private_key_t *this) | |
ea0823df | 206 | { |
e35c3e2a MW |
207 | public_key_t *public; |
208 | chunk_t key; | |
209 | u_char *p; | |
7daf5226 | 210 | |
e35c3e2a MW |
211 | key = chunk_alloc(i2d_EC_PUBKEY(this->ec, NULL)); |
212 | p = key.ptr; | |
213 | i2d_EC_PUBKEY(this->ec, &p); | |
7daf5226 | 214 | |
e35c3e2a MW |
215 | public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ECDSA, |
216 | BUILD_BLOB_ASN1_DER, key, BUILD_END); | |
217 | free(key.ptr); | |
218 | return public; | |
ea0823df TB |
219 | } |
220 | ||
57202484 MW |
221 | METHOD(private_key_t, get_fingerprint, bool, |
222 | private_openssl_ec_private_key_t *this, cred_encoding_type_t type, | |
223 | chunk_t *fingerprint) | |
ea0823df | 224 | { |
b12c6d16 | 225 | return openssl_ec_fingerprint(this->ec, type, fingerprint); |
ea0823df TB |
226 | } |
227 | ||
57202484 MW |
228 | METHOD(private_key_t, get_encoding, bool, |
229 | private_openssl_ec_private_key_t *this, cred_encoding_type_t type, | |
230 | chunk_t *encoding) | |
ea0823df | 231 | { |
e35c3e2a | 232 | u_char *p; |
7daf5226 | 233 | |
b2266280 TB |
234 | if (this->engine) |
235 | { | |
236 | return FALSE; | |
237 | } | |
238 | ||
b12c6d16 MW |
239 | switch (type) |
240 | { | |
da9724e6 MW |
241 | case PRIVKEY_ASN1_DER: |
242 | case PRIVKEY_PEM: | |
b12c6d16 | 243 | { |
29cf15a9 AS |
244 | bool success = TRUE; |
245 | ||
b12c6d16 MW |
246 | *encoding = chunk_alloc(i2d_ECPrivateKey(this->ec, NULL)); |
247 | p = encoding->ptr; | |
248 | i2d_ECPrivateKey(this->ec, &p); | |
29cf15a9 | 249 | |
da9724e6 | 250 | if (type == PRIVKEY_PEM) |
29cf15a9 AS |
251 | { |
252 | chunk_t asn1_encoding = *encoding; | |
253 | ||
da9724e6 MW |
254 | success = lib->encoding->encode(lib->encoding, PRIVKEY_PEM, |
255 | NULL, encoding, CRED_PART_ECDSA_PRIV_ASN1_DER, | |
256 | asn1_encoding, CRED_PART_END); | |
29cf15a9 | 257 | chunk_clear(&asn1_encoding); |
0a4dc787 | 258 | } |
29cf15a9 | 259 | return success; |
b12c6d16 MW |
260 | } |
261 | default: | |
262 | return FALSE; | |
263 | } | |
ea0823df TB |
264 | } |
265 | ||
57202484 MW |
266 | METHOD(private_key_t, get_ref, private_key_t*, |
267 | private_openssl_ec_private_key_t *this) | |
ea0823df TB |
268 | { |
269 | ref_get(&this->ref); | |
57202484 | 270 | return &this->public.key; |
ea0823df TB |
271 | } |
272 | ||
57202484 MW |
273 | METHOD(private_key_t, destroy, void, |
274 | private_openssl_ec_private_key_t *this) | |
ea0823df TB |
275 | { |
276 | if (ref_put(&this->ref)) | |
277 | { | |
278 | if (this->ec) | |
279 | { | |
b12c6d16 | 280 | lib->encoding->clear_cache(lib->encoding, this->ec); |
ea0823df TB |
281 | EC_KEY_free(this->ec); |
282 | } | |
ea0823df TB |
283 | free(this); |
284 | } | |
285 | } | |
286 | ||
287 | /** | |
288 | * Internal generic constructor | |
289 | */ | |
e35c3e2a | 290 | static private_openssl_ec_private_key_t *create_empty(void) |
ea0823df | 291 | { |
57202484 MW |
292 | private_openssl_ec_private_key_t *this; |
293 | ||
294 | INIT(this, | |
ba31fe1f MW |
295 | .public = { |
296 | .key = { | |
297 | .get_type = _get_type, | |
298 | .sign = _sign, | |
299 | .decrypt = _decrypt, | |
300 | .get_keysize = _get_keysize, | |
301 | .get_public_key = _get_public_key, | |
302 | .equals = private_key_equals, | |
303 | .belongs_to = private_key_belongs_to, | |
304 | .get_fingerprint = _get_fingerprint, | |
305 | .has_fingerprint = private_key_has_fingerprint, | |
306 | .get_encoding = _get_encoding, | |
307 | .get_ref = _get_ref, | |
308 | .destroy = _destroy, | |
309 | }, | |
57202484 MW |
310 | }, |
311 | .ref = 1, | |
312 | ); | |
7daf5226 | 313 | |
ea0823df TB |
314 | return this; |
315 | } | |
316 | ||
4a6f97d0 TB |
317 | /* |
318 | * See header. | |
319 | */ | |
b2266280 | 320 | private_key_t *openssl_ec_private_key_create(EVP_PKEY *key, bool engine) |
4a6f97d0 TB |
321 | { |
322 | private_openssl_ec_private_key_t *this; | |
323 | EC_KEY *ec; | |
324 | ||
325 | ec = EVP_PKEY_get1_EC_KEY(key); | |
326 | EVP_PKEY_free(key); | |
327 | if (!ec) | |
328 | { | |
329 | return NULL; | |
330 | } | |
331 | this = create_empty(); | |
332 | this->ec = ec; | |
b2266280 | 333 | this->engine = engine; |
4a6f97d0 TB |
334 | return &this->public.key; |
335 | } | |
336 | ||
337 | /* | |
30c06407 | 338 | * See header. |
08ed551c | 339 | */ |
30c06407 MW |
340 | openssl_ec_private_key_t *openssl_ec_private_key_gen(key_type_t type, |
341 | va_list args) | |
08ed551c | 342 | { |
30c06407 MW |
343 | private_openssl_ec_private_key_t *this; |
344 | u_int key_size = 0; | |
7daf5226 | 345 | |
30c06407 MW |
346 | while (TRUE) |
347 | { | |
348 | switch (va_arg(args, builder_part_t)) | |
349 | { | |
350 | case BUILD_KEY_SIZE: | |
351 | key_size = va_arg(args, u_int); | |
352 | continue; | |
353 | case BUILD_END: | |
354 | break; | |
355 | default: | |
356 | return NULL; | |
357 | } | |
358 | break; | |
359 | } | |
360 | if (!key_size) | |
361 | { | |
362 | return NULL; | |
363 | } | |
364 | this = create_empty(); | |
08ed551c MW |
365 | switch (key_size) |
366 | { | |
367 | case 256: | |
368 | this->ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); | |
369 | break; | |
370 | case 384: | |
371 | this->ec = EC_KEY_new_by_curve_name(NID_secp384r1); | |
372 | break; | |
373 | case 521: | |
374 | this->ec = EC_KEY_new_by_curve_name(NID_secp521r1); | |
375 | break; | |
376 | default: | |
8b0e0910 | 377 | DBG1(DBG_LIB, "EC private key size %d not supported", key_size); |
08ed551c MW |
378 | destroy(this); |
379 | return NULL; | |
380 | } | |
381 | if (EC_KEY_generate_key(this->ec) != 1) | |
382 | { | |
8b0e0910 | 383 | DBG1(DBG_LIB, "EC private key generation failed", key_size); |
08ed551c MW |
384 | destroy(this); |
385 | return NULL; | |
386 | } | |
387 | /* encode as a named curve key (no parameters), uncompressed public key */ | |
388 | EC_KEY_set_asn1_flag(this->ec, OPENSSL_EC_NAMED_CURVE); | |
389 | EC_KEY_set_conv_form(this->ec, POINT_CONVERSION_UNCOMPRESSED); | |
390 | return &this->public; | |
391 | } | |
392 | ||
ea0823df | 393 | /** |
30c06407 | 394 | * See header. |
ea0823df | 395 | */ |
30c06407 MW |
396 | openssl_ec_private_key_t *openssl_ec_private_key_load(key_type_t type, |
397 | va_list args) | |
ea0823df | 398 | { |
30c06407 | 399 | private_openssl_ec_private_key_t *this; |
27f8a61d | 400 | chunk_t par = chunk_empty, key = chunk_empty; |
7daf5226 | 401 | |
30c06407 | 402 | while (TRUE) |
ea0823df | 403 | { |
30c06407 | 404 | switch (va_arg(args, builder_part_t)) |
ea0823df | 405 | { |
27f8a61d TB |
406 | case BUILD_BLOB_ALGID_PARAMS: |
407 | par = va_arg(args, chunk_t); | |
408 | continue; | |
f7c17aa1 | 409 | case BUILD_BLOB_ASN1_DER: |
27f8a61d | 410 | key = va_arg(args, chunk_t); |
30c06407 MW |
411 | continue; |
412 | case BUILD_END: | |
f7c17aa1 | 413 | break; |
30c06407 MW |
414 | default: |
415 | return NULL; | |
ea0823df | 416 | } |
30c06407 | 417 | break; |
ea0823df | 418 | } |
30c06407 MW |
419 | |
420 | this = create_empty(); | |
27f8a61d TB |
421 | |
422 | if (par.ptr) | |
f7c17aa1 | 423 | { |
27f8a61d TB |
424 | this->ec = d2i_ECParameters(NULL, (const u_char**)&par.ptr, par.len); |
425 | if (!this->ec) | |
426 | { | |
427 | goto error; | |
428 | } | |
429 | if (!d2i_ECPrivateKey(&this->ec, (const u_char**)&key.ptr, key.len)) | |
430 | { | |
431 | goto error; | |
432 | } | |
433 | } | |
434 | else | |
435 | { | |
436 | this->ec = d2i_ECPrivateKey(NULL, (const u_char**)&key.ptr, key.len); | |
437 | if (!this->ec) | |
438 | { | |
439 | goto error; | |
440 | } | |
f7c17aa1 | 441 | } |
30c06407 | 442 | if (!EC_KEY_check_key(this->ec)) |
ea0823df | 443 | { |
27f8a61d | 444 | goto error; |
ea0823df | 445 | } |
ea0823df | 446 | return &this->public; |
27f8a61d TB |
447 | |
448 | error: | |
449 | destroy(this); | |
450 | return NULL; | |
ea0823df | 451 | } |
a3a190b7 | 452 | #endif /* OPENSSL_NO_ECDSA */ |