]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 2009 Martin Willi | |
3 | * Copyright (C) 2008 Tobias Brunner | |
4 | * | |
5 | * Copyright (C) secunet Security Networks AG | |
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. | |
16 | */ | |
17 | ||
18 | #include <openssl/opensslconf.h> | |
19 | ||
20 | #ifndef OPENSSL_NO_ECDSA | |
21 | ||
22 | #include "openssl_ec_public_key.h" | |
23 | #include "openssl_util.h" | |
24 | ||
25 | #include <utils/debug.h> | |
26 | ||
27 | #include <openssl/evp.h> | |
28 | #include <openssl/ecdsa.h> | |
29 | #include <openssl/x509.h> | |
30 | ||
31 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L | |
32 | #include <openssl/core_names.h> | |
33 | #endif | |
34 | ||
35 | #if OPENSSL_VERSION_NUMBER < 0x10100000L | |
36 | OPENSSL_KEY_FALLBACK(ECDSA_SIG, r, s) | |
37 | #endif | |
38 | ||
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; | |
49 | ||
50 | /** | |
51 | * EC key object | |
52 | */ | |
53 | EVP_PKEY *key; | |
54 | ||
55 | /** | |
56 | * reference counter | |
57 | */ | |
58 | refcount_t ref; | |
59 | }; | |
60 | ||
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 | ||
93 | /** | |
94 | * Verification of a signature as in RFC 4754 | |
95 | */ | |
96 | static bool verify_signature(private_openssl_ec_public_key_t *this, | |
97 | int nid_hash, chunk_t data, chunk_t signature) | |
98 | { | |
99 | EVP_PKEY_CTX *ctx; | |
100 | BIGNUM *r, *s; | |
101 | ECDSA_SIG *sig; | |
102 | chunk_t der_sig; | |
103 | bool valid = FALSE; | |
104 | ||
105 | sig = ECDSA_SIG_new(); | |
106 | if (sig) | |
107 | { | |
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)) | |
118 | { | |
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); | |
133 | } | |
134 | ECDSA_SIG_free(sig); | |
135 | } | |
136 | return valid; | |
137 | } | |
138 | ||
139 | /** | |
140 | * Check that the given key's curve matches a specific one. Also used by | |
141 | * private key. | |
142 | */ | |
143 | bool openssl_check_ec_key_curve(EVP_PKEY *key, int nid_curve) | |
144 | { | |
145 | EC_GROUP *req_group, *my_group = NULL; | |
146 | bool matches = FALSE; | |
147 | ||
148 | req_group = EC_GROUP_new_by_curve_name(nid_curve); | |
149 | if (!req_group) | |
150 | { | |
151 | goto error; | |
152 | } | |
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)) | |
162 | { | |
163 | goto error; | |
164 | } | |
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) | |
176 | { | |
177 | matches = TRUE; | |
178 | } | |
179 | ||
180 | error: | |
181 | EC_GROUP_free(my_group); | |
182 | EC_GROUP_free(req_group); | |
183 | return matches; | |
184 | } | |
185 | ||
186 | /** | |
187 | * Verify a RFC 4754 signature for a specified curve and hash algorithm | |
188 | */ | |
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) | |
192 | { | |
193 | if (!openssl_check_ec_key_curve(this->key, nid_curve)) | |
194 | { | |
195 | DBG1(DBG_LIB, "signature scheme %N not supported by key", | |
196 | signature_scheme_names, scheme); | |
197 | return FALSE; | |
198 | } | |
199 | return verify_signature(this, nid_hash, data, signature); | |
200 | } | |
201 | ||
202 | METHOD(public_key_t, get_type, key_type_t, | |
203 | private_openssl_ec_public_key_t *this) | |
204 | { | |
205 | return KEY_ECDSA; | |
206 | } | |
207 | ||
208 | METHOD(public_key_t, verify, bool, | |
209 | private_openssl_ec_public_key_t *this, signature_scheme_t scheme, | |
210 | void *params, chunk_t data, chunk_t signature) | |
211 | { | |
212 | switch (scheme) | |
213 | { | |
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); | |
222 | case SIGN_ECDSA_WITH_NULL: | |
223 | return verify_signature(this, 0, data, signature); | |
224 | case SIGN_ECDSA_256: | |
225 | return verify_curve_signature(this, scheme, NID_sha256, | |
226 | NID_X9_62_prime256v1, data, signature); | |
227 | case SIGN_ECDSA_384: | |
228 | return verify_curve_signature(this, scheme, NID_sha384, | |
229 | NID_secp384r1, data, signature); | |
230 | case SIGN_ECDSA_521: | |
231 | return verify_curve_signature(this, scheme, NID_sha512, | |
232 | NID_secp521r1, data, signature); | |
233 | default: | |
234 | DBG1(DBG_LIB, "signature scheme %N not supported in EC", | |
235 | signature_scheme_names, scheme); | |
236 | return FALSE; | |
237 | } | |
238 | } | |
239 | ||
240 | METHOD(public_key_t, encrypt, bool, | |
241 | private_openssl_ec_public_key_t *this, encryption_scheme_t scheme, | |
242 | void *params, chunk_t crypto, chunk_t *plain) | |
243 | { | |
244 | DBG1(DBG_LIB, "EC public key encryption not implemented"); | |
245 | return FALSE; | |
246 | } | |
247 | ||
248 | METHOD(public_key_t, get_keysize, int, | |
249 | private_openssl_ec_public_key_t *this) | |
250 | { | |
251 | return EVP_PKEY_bits(this->key); | |
252 | } | |
253 | ||
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) | |
257 | { | |
258 | return openssl_fingerprint(this->key, type, fingerprint); | |
259 | } | |
260 | ||
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) | |
264 | { | |
265 | bool success = TRUE; | |
266 | ||
267 | *encoding = openssl_i2chunk(PUBKEY, this->key); | |
268 | ||
269 | if (type != PUBKEY_SPKI_ASN1_DER) | |
270 | { | |
271 | chunk_t asn1_encoding = *encoding; | |
272 | ||
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); | |
277 | } | |
278 | return success; | |
279 | } | |
280 | ||
281 | METHOD(public_key_t, get_ref, public_key_t*, | |
282 | private_openssl_ec_public_key_t *this) | |
283 | { | |
284 | ref_get(&this->ref); | |
285 | return &this->public.key; | |
286 | } | |
287 | ||
288 | METHOD(public_key_t, destroy, void, | |
289 | private_openssl_ec_public_key_t *this) | |
290 | { | |
291 | if (ref_put(&this->ref)) | |
292 | { | |
293 | if (this->key) | |
294 | { | |
295 | lib->encoding->clear_cache(lib->encoding, this->key); | |
296 | EVP_PKEY_free(this->key); | |
297 | } | |
298 | free(this); | |
299 | } | |
300 | } | |
301 | ||
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 | ||
322 | /** | |
323 | * See header. | |
324 | */ | |
325 | openssl_ec_public_key_t *openssl_ec_public_key_load(key_type_t type, | |
326 | va_list args) | |
327 | { | |
328 | private_openssl_ec_public_key_t *this; | |
329 | chunk_t blob = chunk_empty; | |
330 | EVP_PKEY *key; | |
331 | ||
332 | while (TRUE) | |
333 | { | |
334 | switch (va_arg(args, builder_part_t)) | |
335 | { | |
336 | case BUILD_BLOB_ASN1_DER: | |
337 | blob = va_arg(args, chunk_t); | |
338 | continue; | |
339 | case BUILD_END: | |
340 | break; | |
341 | default: | |
342 | return NULL; | |
343 | } | |
344 | break; | |
345 | } | |
346 | key = d2i_PUBKEY(NULL, (const u_char**)&blob.ptr, blob.len); | |
347 | if (!key || EVP_PKEY_base_id(key) != EVP_PKEY_EC || | |
348 | openssl_check_explicit_params(key)) | |
349 | { | |
350 | EVP_PKEY_free(key); | |
351 | return NULL; | |
352 | } | |
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 | ); | |
372 | return &this->public; | |
373 | } | |
374 | ||
375 | #endif /* OPENSSL_NO_ECDSA */ |