]>
Commit | Line | Data |
---|---|---|
ea0823df | 1 | /* |
e35c3e2a | 2 | * Copyright (C) 2009 Martin Willi |
ea0823df | 3 | * Copyright (C) 2008 Tobias Brunner |
19ef2aec TB |
4 | * |
5 | * Copyright (C) secunet Security Networks AG | |
ea0823df TB |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2 of the License, or (at your | |
10 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 | * for more details. | |
ea0823df TB |
16 | */ |
17 | ||
5a367e99 TB |
18 | #include <openssl/opensslconf.h> |
19 | ||
a3a190b7 | 20 | #ifndef OPENSSL_NO_ECDSA |
5a367e99 | 21 | |
ea0823df TB |
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> | |
29 | #include <openssl/x509.h> | |
30 | ||
293a912c TB |
31 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L |
32 | #include <openssl/core_names.h> | |
33 | #endif | |
34 | ||
6688f798 TB |
35 | #if OPENSSL_VERSION_NUMBER < 0x10100000L |
36 | OPENSSL_KEY_FALLBACK(ECDSA_SIG, r, s) | |
37 | #endif | |
38 | ||
ea0823df TB |
39 | typedef struct private_openssl_ec_public_key_t private_openssl_ec_public_key_t; |
40 | ||
41 | /** | |
42 | * Private data structure with signing context. | |
43 | */ | |
44 | struct private_openssl_ec_public_key_t { | |
45 | /** | |
46 | * Public interface for this signer. | |
47 | */ | |
48 | openssl_ec_public_key_t public; | |
7daf5226 | 49 | |
ea0823df TB |
50 | /** |
51 | * EC key object | |
52 | */ | |
293a912c | 53 | EVP_PKEY *key; |
7daf5226 | 54 | |
ea0823df TB |
55 | /** |
56 | * reference counter | |
57 | */ | |
58 | refcount_t ref; | |
59 | }; | |
60 | ||
293a912c TB |
61 | /** |
62 | * Verification of a DER encoded signature as in RFC 3279 | |
63 | */ | |
64 | static bool verify_der_signature(private_openssl_ec_public_key_t *this, | |
65 | int nid_hash, chunk_t data, chunk_t signature) | |
66 | { | |
67 | EVP_MD_CTX *ctx; | |
68 | const EVP_MD *md; | |
69 | ||
70 | /* remove any preceding 0-bytes from signature */ | |
71 | while (signature.len && signature.ptr[0] == 0x00) | |
72 | { | |
73 | signature = chunk_skip(signature, 1); | |
74 | } | |
75 | md = EVP_get_digestbynid(nid_hash); | |
76 | if (!md) | |
77 | { | |
78 | return FALSE; | |
79 | } | |
80 | ctx = EVP_MD_CTX_create(); | |
81 | if (!ctx || | |
82 | EVP_DigestVerifyInit(ctx, NULL, md, NULL, this->key) <= 0 || | |
83 | EVP_DigestVerifyUpdate(ctx, data.ptr, data.len) <= 0 || | |
84 | EVP_DigestVerifyFinal(ctx, signature.ptr, signature.len) != 1) | |
85 | { | |
86 | EVP_MD_CTX_destroy(ctx); | |
87 | return FALSE; | |
88 | } | |
89 | EVP_MD_CTX_destroy(ctx); | |
90 | return TRUE; | |
91 | } | |
92 | ||
ea0823df TB |
93 | /** |
94 | * Verification of a signature as in RFC 4754 | |
95 | */ | |
96 | static bool verify_signature(private_openssl_ec_public_key_t *this, | |
293a912c | 97 | int nid_hash, chunk_t data, chunk_t signature) |
ea0823df | 98 | { |
293a912c | 99 | EVP_PKEY_CTX *ctx; |
6688f798 | 100 | BIGNUM *r, *s; |
472cb4ce | 101 | ECDSA_SIG *sig; |
293a912c | 102 | chunk_t der_sig; |
6688f798 | 103 | bool valid = FALSE; |
7daf5226 | 104 | |
ea0823df | 105 | sig = ECDSA_SIG_new(); |
ea0823df TB |
106 | if (sig) |
107 | { | |
6688f798 TB |
108 | r = BN_new(); |
109 | s = BN_new(); | |
110 | if (!openssl_bn_split(signature, r, s)) | |
111 | { | |
112 | BN_free(r); | |
113 | BN_free(s); | |
114 | ECDSA_SIG_free(sig); | |
115 | return FALSE; | |
116 | } | |
117 | if (ECDSA_SIG_set0(sig, r, s)) | |
472cb4ce | 118 | { |
293a912c TB |
119 | der_sig = openssl_i2chunk(ECDSA_SIG, sig); |
120 | if (!nid_hash) | |
121 | { /* EVP_DigestVerify*() has issues with NULL EVP_MD */ | |
122 | ctx = EVP_PKEY_CTX_new(this->key, NULL); | |
123 | valid = ctx && EVP_PKEY_verify_init(ctx) > 0 && | |
124 | EVP_PKEY_verify(ctx, der_sig.ptr, der_sig.len, | |
125 | data.ptr, data.len) > 0; | |
126 | EVP_PKEY_CTX_free(ctx); | |
127 | } | |
128 | else | |
129 | { | |
130 | valid = verify_der_signature(this, nid_hash, data, der_sig); | |
131 | } | |
132 | chunk_free(&der_sig); | |
472cb4ce | 133 | } |
ea0823df TB |
134 | ECDSA_SIG_free(sig); |
135 | } | |
10b2898d MW |
136 | return valid; |
137 | } | |
138 | ||
139 | /** | |
293a912c TB |
140 | * Check that the given key's curve matches a specific one. Also used by |
141 | * private key. | |
10b2898d | 142 | */ |
293a912c | 143 | bool openssl_check_ec_key_curve(EVP_PKEY *key, int nid_curve) |
10b2898d | 144 | { |
293a912c TB |
145 | EC_GROUP *req_group, *my_group = NULL; |
146 | bool matches = FALSE; | |
7daf5226 | 147 | |
10b2898d MW |
148 | req_group = EC_GROUP_new_by_curve_name(nid_curve); |
149 | if (!req_group) | |
150 | { | |
293a912c | 151 | goto error; |
10b2898d | 152 | } |
293a912c TB |
153 | |
154 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | |
155 | char name[BUF_LEN]; | |
156 | OSSL_PARAM params[] = { | |
157 | OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, name, sizeof(name)), | |
158 | OSSL_PARAM_END, | |
159 | }; | |
160 | ||
161 | if (!EVP_PKEY_get_group_name(key, name, sizeof(name), NULL)) | |
10b2898d | 162 | { |
293a912c | 163 | goto error; |
10b2898d | 164 | } |
293a912c TB |
165 | my_group = EC_GROUP_new_from_params(params, NULL, NULL); |
166 | #elif OPENSSL_VERSION_NUMBER >= 0x1010000fL | |
167 | EC_KEY *ec = EVP_PKEY_get0_EC_KEY(key); | |
168 | my_group = EC_GROUP_dup(EC_KEY_get0_group(ec)); | |
169 | #else | |
170 | EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key); | |
171 | my_group = EC_GROUP_dup(EC_KEY_get0_group(ec)); | |
172 | EC_KEY_free(ec); | |
173 | #endif | |
174 | ||
175 | if (EC_GROUP_cmp(my_group, req_group, NULL) == 0) | |
11e6d285 | 176 | { |
293a912c | 177 | matches = TRUE; |
11e6d285 | 178 | } |
293a912c TB |
179 | |
180 | error: | |
181 | EC_GROUP_free(my_group); | |
182 | EC_GROUP_free(req_group); | |
183 | return matches; | |
ea0823df TB |
184 | } |
185 | ||
ea0823df | 186 | /** |
293a912c | 187 | * Verify a RFC 4754 signature for a specified curve and hash algorithm |
ea0823df | 188 | */ |
293a912c TB |
189 | static bool verify_curve_signature(private_openssl_ec_public_key_t *this, |
190 | signature_scheme_t scheme, int nid_hash, | |
191 | int nid_curve, chunk_t data, chunk_t signature) | |
ea0823df | 192 | { |
293a912c | 193 | if (!openssl_check_ec_key_curve(this->key, nid_curve)) |
ea0823df | 194 | { |
293a912c TB |
195 | DBG1(DBG_LIB, "signature scheme %N not supported by key", |
196 | signature_scheme_names, scheme); | |
197 | return FALSE; | |
ea0823df | 198 | } |
293a912c | 199 | return verify_signature(this, nid_hash, data, signature); |
ea0823df TB |
200 | } |
201 | ||
57202484 MW |
202 | METHOD(public_key_t, get_type, key_type_t, |
203 | private_openssl_ec_public_key_t *this) | |
ea0823df TB |
204 | { |
205 | return KEY_ECDSA; | |
206 | } | |
207 | ||
57202484 MW |
208 | METHOD(public_key_t, verify, bool, |
209 | private_openssl_ec_public_key_t *this, signature_scheme_t scheme, | |
a413571f | 210 | void *params, chunk_t data, chunk_t signature) |
ea0823df TB |
211 | { |
212 | switch (scheme) | |
213 | { | |
472cb4ce MW |
214 | case SIGN_ECDSA_WITH_SHA1_DER: |
215 | return verify_der_signature(this, NID_sha1, data, signature); | |
216 | case SIGN_ECDSA_WITH_SHA256_DER: | |
217 | return verify_der_signature(this, NID_sha256, data, signature); | |
218 | case SIGN_ECDSA_WITH_SHA384_DER: | |
219 | return verify_der_signature(this, NID_sha384, data, signature); | |
220 | case SIGN_ECDSA_WITH_SHA512_DER: | |
221 | return verify_der_signature(this, NID_sha512, data, signature); | |
11e6d285 | 222 | case SIGN_ECDSA_WITH_NULL: |
293a912c | 223 | return verify_signature(this, 0, data, signature); |
ea0823df | 224 | case SIGN_ECDSA_256: |
c8128024 AS |
225 | return verify_curve_signature(this, scheme, NID_sha256, |
226 | NID_X9_62_prime256v1, data, signature); | |
ea0823df | 227 | case SIGN_ECDSA_384: |
c8128024 AS |
228 | return verify_curve_signature(this, scheme, NID_sha384, |
229 | NID_secp384r1, data, signature); | |
ea0823df | 230 | case SIGN_ECDSA_521: |
c8128024 AS |
231 | return verify_curve_signature(this, scheme, NID_sha512, |
232 | NID_secp521r1, data, signature); | |
ea0823df | 233 | default: |
8b0e0910 | 234 | DBG1(DBG_LIB, "signature scheme %N not supported in EC", |
ea0823df TB |
235 | signature_scheme_names, scheme); |
236 | return FALSE; | |
237 | } | |
238 | } | |
239 | ||
57202484 | 240 | METHOD(public_key_t, encrypt, bool, |
33ddaaab | 241 | private_openssl_ec_public_key_t *this, encryption_scheme_t scheme, |
4abb29f6 | 242 | void *params, chunk_t crypto, chunk_t *plain) |
ea0823df | 243 | { |
8b0e0910 | 244 | DBG1(DBG_LIB, "EC public key encryption not implemented"); |
ea0823df TB |
245 | return FALSE; |
246 | } | |
247 | ||
a944d209 | 248 | METHOD(public_key_t, get_keysize, int, |
57202484 | 249 | private_openssl_ec_public_key_t *this) |
ea0823df | 250 | { |
293a912c | 251 | return EVP_PKEY_bits(this->key); |
b12c6d16 MW |
252 | } |
253 | ||
57202484 MW |
254 | METHOD(public_key_t, get_fingerprint, bool, |
255 | private_openssl_ec_public_key_t *this, cred_encoding_type_t type, | |
256 | chunk_t *fingerprint) | |
b12c6d16 | 257 | { |
293a912c | 258 | return openssl_fingerprint(this->key, type, fingerprint); |
ea0823df TB |
259 | } |
260 | ||
57202484 MW |
261 | METHOD(public_key_t, get_encoding, bool, |
262 | private_openssl_ec_public_key_t *this, cred_encoding_type_t type, | |
263 | chunk_t *encoding) | |
ea0823df | 264 | { |
d6b3cc87 | 265 | bool success = TRUE; |
7daf5226 | 266 | |
293a912c | 267 | *encoding = openssl_i2chunk(PUBKEY, this->key); |
29cf15a9 | 268 | |
d6b3cc87 TB |
269 | if (type != PUBKEY_SPKI_ASN1_DER) |
270 | { | |
271 | chunk_t asn1_encoding = *encoding; | |
29cf15a9 | 272 | |
d6b3cc87 TB |
273 | success = lib->encoding->encode(lib->encoding, type, |
274 | NULL, encoding, CRED_PART_ECDSA_PUB_ASN1_DER, | |
275 | asn1_encoding, CRED_PART_END); | |
276 | chunk_clear(&asn1_encoding); | |
b12c6d16 | 277 | } |
d6b3cc87 | 278 | return success; |
ea0823df TB |
279 | } |
280 | ||
57202484 MW |
281 | METHOD(public_key_t, get_ref, public_key_t*, |
282 | private_openssl_ec_public_key_t *this) | |
ea0823df TB |
283 | { |
284 | ref_get(&this->ref); | |
57202484 | 285 | return &this->public.key; |
ea0823df TB |
286 | } |
287 | ||
57202484 MW |
288 | METHOD(public_key_t, destroy, void, |
289 | private_openssl_ec_public_key_t *this) | |
ea0823df TB |
290 | { |
291 | if (ref_put(&this->ref)) | |
292 | { | |
293a912c | 293 | if (this->key) |
ea0823df | 294 | { |
293a912c TB |
295 | lib->encoding->clear_cache(lib->encoding, this->key); |
296 | EVP_PKEY_free(this->key); | |
ea0823df | 297 | } |
ea0823df TB |
298 | free(this); |
299 | } | |
300 | } | |
301 | ||
2bccdefc TB |
302 | /** |
303 | * Check whether the EC key was decoded with explicit curve parameters instead | |
304 | * of a named curve. | |
305 | */ | |
306 | bool openssl_check_explicit_params(const EVP_PKEY *key) | |
307 | { | |
308 | int explicit = 0; | |
309 | ||
310 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | |
311 | if (!EVP_PKEY_get_int_param(key, OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, | |
312 | &explicit)) | |
313 | { | |
314 | return FALSE; | |
315 | } | |
316 | #elif OPENSSL_VERSION_NUMBER >= 0x1010108fL | |
317 | explicit = EC_KEY_decoded_from_explicit_params(EVP_PKEY_get0_EC_KEY((EVP_PKEY*)key)); | |
318 | #endif | |
319 | return explicit == 1; | |
320 | } | |
321 | ||
ea0823df | 322 | /** |
30c06407 | 323 | * See header. |
ea0823df | 324 | */ |
30c06407 MW |
325 | openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type, |
326 | va_list args) | |
ea0823df | 327 | { |
30c06407 MW |
328 | private_openssl_ec_public_key_t *this; |
329 | chunk_t blob = chunk_empty; | |
293a912c | 330 | EVP_PKEY *key; |
e35c3e2a | 331 | |
30c06407 | 332 | while (TRUE) |
ea0823df | 333 | { |
30c06407 | 334 | switch (va_arg(args, builder_part_t)) |
ea0823df | 335 | { |
f7c17aa1 | 336 | case BUILD_BLOB_ASN1_DER: |
30c06407 MW |
337 | blob = va_arg(args, chunk_t); |
338 | continue; | |
339 | case BUILD_END: | |
f7c17aa1 | 340 | break; |
30c06407 MW |
341 | default: |
342 | return NULL; | |
ea0823df | 343 | } |
30c06407 | 344 | break; |
ea0823df | 345 | } |
293a912c | 346 | key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len); |
2bccdefc TB |
347 | if (!key || EVP_PKEY_base_id(key) != EVP_PKEY_EC || |
348 | openssl_check_explicit_params(key)) | |
ea0823df | 349 | { |
293a912c | 350 | EVP_PKEY_free(key); |
ea0823df TB |
351 | return NULL; |
352 | } | |
293a912c TB |
353 | |
354 | INIT(this, | |
355 | .public = { | |
356 | .key = { | |
357 | .get_type = _get_type, | |
358 | .verify = _verify, | |
359 | .encrypt = _encrypt, | |
360 | .get_keysize = _get_keysize, | |
361 | .equals = public_key_equals, | |
362 | .get_fingerprint = _get_fingerprint, | |
363 | .has_fingerprint = public_key_has_fingerprint, | |
364 | .get_encoding = _get_encoding, | |
365 | .get_ref = _get_ref, | |
366 | .destroy = _destroy, | |
367 | }, | |
368 | }, | |
369 | .ref = 1, | |
370 | .key = key, | |
371 | ); | |
ea0823df TB |
372 | return &this->public; |
373 | } | |
374 | ||
293a912c | 375 | #endif /* OPENSSL_NO_ECDSA */ |