/src/bind9/lib/dns/opensslecdsa_link.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) Internet Systems Consortium, Inc. ("ISC") |
3 | | * |
4 | | * SPDX-License-Identifier: MPL-2.0 |
5 | | * |
6 | | * This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, you can obtain one at https://mozilla.org/MPL/2.0/. |
9 | | * |
10 | | * See the COPYRIGHT file distributed with this work for additional |
11 | | * information regarding copyright ownership. |
12 | | */ |
13 | | |
14 | | /*! \file */ |
15 | | |
16 | | #include <stdbool.h> |
17 | | |
18 | | #include <openssl/bn.h> |
19 | | #include <openssl/ecdsa.h> |
20 | | #include <openssl/evp.h> |
21 | | #include <openssl/objects.h> |
22 | | |
23 | | #include <isc/crypto.h> |
24 | | #include <isc/md.h> |
25 | | #include <isc/mem.h> |
26 | | #include <isc/ossl_wrap.h> |
27 | | #include <isc/result.h> |
28 | | #include <isc/safe.h> |
29 | | #include <isc/string.h> |
30 | | #include <isc/util.h> |
31 | | |
32 | | #include <dns/keyvalues.h> |
33 | | |
34 | | #include "dst_internal.h" |
35 | | #include "dst_openssl.h" |
36 | | #include "dst_parse.h" |
37 | | #include "openssl_shim.h" |
38 | | |
39 | | #ifndef NID_X9_62_prime256v1 |
40 | | #error "P-256 group is not known (NID_X9_62_prime256v1)" |
41 | | #endif /* ifndef NID_X9_62_prime256v1 */ |
42 | | #ifndef NID_secp384r1 |
43 | | #error "P-384 group is not known (NID_secp384r1)" |
44 | | #endif /* ifndef NID_secp384r1 */ |
45 | | |
46 | | #define MAX_PUBKEY_SIZE DNS_KEY_ECDSA384SIZE |
47 | | |
48 | | #define MAX_PRIVKEY_SIZE (MAX_PUBKEY_SIZE / 2) |
49 | | |
50 | | static bool |
51 | 0 | opensslecdsa_valid_key_alg(unsigned int key_alg) { |
52 | 0 | switch (key_alg) { |
53 | 0 | case DST_ALG_ECDSA256: |
54 | 0 | case DST_ALG_ECDSA384: |
55 | 0 | return true; |
56 | 0 | default: |
57 | 0 | return false; |
58 | 0 | } |
59 | 0 | } |
60 | | |
61 | | static size_t |
62 | 0 | opensslecdsa_key_alg_to_publickey_size(unsigned int key_alg) { |
63 | 0 | switch (key_alg) { |
64 | 0 | case DST_ALG_ECDSA256: |
65 | 0 | return DNS_KEY_ECDSA256SIZE; |
66 | 0 | case DST_ALG_ECDSA384: |
67 | 0 | return DNS_KEY_ECDSA384SIZE; |
68 | 0 | default: |
69 | 0 | UNREACHABLE(); |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | | static int |
74 | 0 | BN_bn2bin_fixed(const BIGNUM *bn, unsigned char *buf, int size) { |
75 | 0 | int bytes = size - BN_num_bytes(bn); |
76 | |
|
77 | 0 | INSIST(bytes >= 0); |
78 | |
|
79 | 0 | while (bytes-- > 0) { |
80 | 0 | *buf++ = 0; |
81 | 0 | } |
82 | 0 | BN_bn2bin(bn, buf); |
83 | 0 | return size; |
84 | 0 | } |
85 | | |
86 | | static isc_result_t |
87 | 0 | opensslecdsa_createctx(dst_key_t *key, dst_context_t *dctx) { |
88 | 0 | isc_result_t result = ISC_R_SUCCESS; |
89 | 0 | EVP_MD_CTX *evp_md_ctx; |
90 | 0 | EVP_PKEY_CTX *pctx = NULL; |
91 | 0 | const EVP_MD *type = NULL; |
92 | 0 | const char *md = NULL; |
93 | |
|
94 | 0 | UNUSED(key); |
95 | 0 | REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg)); |
96 | 0 | REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY); |
97 | |
|
98 | 0 | evp_md_ctx = EVP_MD_CTX_create(); |
99 | 0 | if (evp_md_ctx == NULL) { |
100 | 0 | return dst__openssl_toresult(ISC_R_NOMEMORY); |
101 | 0 | } |
102 | 0 | if (dctx->key->key_alg == DST_ALG_ECDSA256) { |
103 | 0 | type = isc__crypto_md[ISC_MD_SHA256]; |
104 | 0 | md = "SHA256"; |
105 | 0 | } else { |
106 | 0 | type = isc__crypto_md[ISC_MD_SHA384]; |
107 | 0 | md = "SHA384"; |
108 | 0 | } |
109 | |
|
110 | 0 | if (dctx->use == DO_SIGN) { |
111 | 0 | if (EVP_DigestSignInit(evp_md_ctx, &pctx, type, NULL, |
112 | 0 | dctx->key->keydata.pkeypair.priv) != 1) |
113 | 0 | { |
114 | 0 | EVP_MD_CTX_destroy(evp_md_ctx); |
115 | 0 | return dst__openssl_toresult3(dctx->category, |
116 | 0 | "EVP_DigestSignInit", |
117 | 0 | ISC_R_FAILURE); |
118 | 0 | } |
119 | | |
120 | 0 | if (!isc_crypto_fips_mode()) { |
121 | 0 | result = isc_ossl_wrap_ecdsa_set_deterministic(pctx, |
122 | 0 | md); |
123 | 0 | if (result != ISC_R_SUCCESS && |
124 | 0 | result != ISC_R_NOTIMPLEMENTED) |
125 | 0 | { |
126 | 0 | EVP_MD_CTX_destroy(evp_md_ctx); |
127 | 0 | return result; |
128 | 0 | } |
129 | 0 | } |
130 | |
|
131 | 0 | } else { |
132 | 0 | if (EVP_DigestVerifyInit(evp_md_ctx, NULL, type, NULL, |
133 | 0 | dctx->key->keydata.pkeypair.pub) != 1) |
134 | 0 | { |
135 | 0 | EVP_MD_CTX_destroy(evp_md_ctx); |
136 | 0 | return dst__openssl_toresult3(dctx->category, |
137 | 0 | "EVP_DigestVerifyInit", |
138 | 0 | ISC_R_FAILURE); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | 0 | dctx->ctxdata.evp_md_ctx = evp_md_ctx; |
143 | 0 | result = ISC_R_SUCCESS; |
144 | |
|
145 | 0 | return result; |
146 | 0 | } |
147 | | |
148 | | static void |
149 | 0 | opensslecdsa_destroyctx(dst_context_t *dctx) { |
150 | 0 | EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; |
151 | |
|
152 | 0 | REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg)); |
153 | 0 | REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY); |
154 | |
|
155 | 0 | if (evp_md_ctx != NULL) { |
156 | 0 | EVP_MD_CTX_destroy(evp_md_ctx); |
157 | 0 | dctx->ctxdata.evp_md_ctx = NULL; |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | | static isc_result_t |
162 | 0 | opensslecdsa_adddata(dst_context_t *dctx, const isc_region_t *data) { |
163 | 0 | isc_result_t result = ISC_R_SUCCESS; |
164 | 0 | EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; |
165 | |
|
166 | 0 | REQUIRE(opensslecdsa_valid_key_alg(dctx->key->key_alg)); |
167 | 0 | REQUIRE(dctx->use == DO_SIGN || dctx->use == DO_VERIFY); |
168 | |
|
169 | 0 | if (dctx->use == DO_SIGN) { |
170 | 0 | if (EVP_DigestSignUpdate(evp_md_ctx, data->base, |
171 | 0 | data->length) != 1) |
172 | 0 | { |
173 | 0 | return dst__openssl_toresult3(dctx->category, |
174 | 0 | "EVP_DigestSignUpdate", |
175 | 0 | ISC_R_FAILURE); |
176 | 0 | } |
177 | 0 | } else { |
178 | 0 | if (EVP_DigestVerifyUpdate(evp_md_ctx, data->base, |
179 | 0 | data->length) != 1) |
180 | 0 | { |
181 | 0 | return dst__openssl_toresult3(dctx->category, |
182 | 0 | "EVP_DigestVerifyUpdate", |
183 | 0 | ISC_R_FAILURE); |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | 0 | return result; |
188 | 0 | } |
189 | | |
190 | | static isc_result_t |
191 | 0 | opensslecdsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { |
192 | 0 | isc_result_t result; |
193 | 0 | dst_key_t *key = dctx->key; |
194 | 0 | isc_region_t region; |
195 | 0 | EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; |
196 | 0 | ECDSA_SIG *ecdsasig = NULL; |
197 | 0 | size_t siglen, sigder_len = 0, sigder_alloced = 0; |
198 | 0 | unsigned char *sigder = NULL; |
199 | 0 | const unsigned char *sigder_copy; |
200 | 0 | const BIGNUM *r, *s; |
201 | |
|
202 | 0 | REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); |
203 | 0 | REQUIRE(dctx->use == DO_SIGN); |
204 | |
|
205 | 0 | if (key->key_alg == DST_ALG_ECDSA256) { |
206 | 0 | siglen = DNS_SIG_ECDSA256SIZE; |
207 | 0 | } else { |
208 | 0 | siglen = DNS_SIG_ECDSA384SIZE; |
209 | 0 | } |
210 | |
|
211 | 0 | isc_buffer_availableregion(sig, ®ion); |
212 | 0 | if (region.length < siglen) { |
213 | 0 | CLEANUP(ISC_R_NOSPACE); |
214 | 0 | } |
215 | | |
216 | 0 | if (EVP_DigestSignFinal(evp_md_ctx, NULL, &sigder_len) != 1) { |
217 | 0 | CLEANUP(dst__openssl_toresult3( |
218 | 0 | dctx->category, "EVP_DigestSignFinal", ISC_R_FAILURE)); |
219 | 0 | } |
220 | 0 | if (sigder_len == 0) { |
221 | 0 | CLEANUP(ISC_R_FAILURE); |
222 | 0 | } |
223 | 0 | sigder = isc_mem_get(dctx->mctx, sigder_len); |
224 | 0 | sigder_alloced = sigder_len; |
225 | 0 | if (EVP_DigestSignFinal(evp_md_ctx, sigder, &sigder_len) != 1) { |
226 | 0 | CLEANUP(dst__openssl_toresult3( |
227 | 0 | dctx->category, "EVP_DigestSignFinal", ISC_R_FAILURE)); |
228 | 0 | } |
229 | 0 | sigder_copy = sigder; |
230 | 0 | if (d2i_ECDSA_SIG(&ecdsasig, &sigder_copy, sigder_len) == NULL) { |
231 | 0 | CLEANUP(dst__openssl_toresult3(dctx->category, "d2i_ECDSA_SIG", |
232 | 0 | ISC_R_FAILURE)); |
233 | 0 | } |
234 | | |
235 | 0 | ECDSA_SIG_get0(ecdsasig, &r, &s); |
236 | 0 | BN_bn2bin_fixed(r, region.base, siglen / 2); |
237 | 0 | isc_region_consume(®ion, siglen / 2); |
238 | 0 | BN_bn2bin_fixed(s, region.base, siglen / 2); |
239 | 0 | isc_region_consume(®ion, siglen / 2); |
240 | 0 | ECDSA_SIG_free(ecdsasig); |
241 | 0 | isc_buffer_add(sig, siglen); |
242 | 0 | result = ISC_R_SUCCESS; |
243 | |
|
244 | 0 | cleanup: |
245 | 0 | if (sigder != NULL && sigder_alloced != 0) { |
246 | 0 | isc_mem_put(dctx->mctx, sigder, sigder_alloced); |
247 | 0 | } |
248 | |
|
249 | 0 | return result; |
250 | 0 | } |
251 | | |
252 | | static isc_result_t |
253 | 0 | opensslecdsa_verify(dst_context_t *dctx, const isc_region_t *sig) { |
254 | 0 | isc_result_t result; |
255 | 0 | dst_key_t *key = dctx->key; |
256 | 0 | int status; |
257 | 0 | unsigned char *cp = sig->base; |
258 | 0 | ECDSA_SIG *ecdsasig = NULL; |
259 | 0 | EVP_MD_CTX *evp_md_ctx = dctx->ctxdata.evp_md_ctx; |
260 | 0 | size_t siglen, sigder_len = 0, sigder_alloced = 0; |
261 | 0 | unsigned char *sigder = NULL; |
262 | 0 | unsigned char *sigder_copy; |
263 | 0 | BIGNUM *r = NULL, *s = NULL; |
264 | |
|
265 | 0 | REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); |
266 | 0 | REQUIRE(dctx->use == DO_VERIFY); |
267 | |
|
268 | 0 | if (key->key_alg == DST_ALG_ECDSA256) { |
269 | 0 | siglen = DNS_SIG_ECDSA256SIZE; |
270 | 0 | } else { |
271 | 0 | siglen = DNS_SIG_ECDSA384SIZE; |
272 | 0 | } |
273 | |
|
274 | 0 | if (sig->length != siglen) { |
275 | 0 | CLEANUP(DST_R_VERIFYFAILURE); |
276 | 0 | } |
277 | | |
278 | 0 | ecdsasig = ECDSA_SIG_new(); |
279 | 0 | if (ecdsasig == NULL) { |
280 | 0 | CLEANUP(dst__openssl_toresult(ISC_R_NOMEMORY)); |
281 | 0 | } |
282 | 0 | r = BN_bin2bn(cp, siglen / 2, NULL); |
283 | 0 | cp += siglen / 2; |
284 | 0 | s = BN_bin2bn(cp, siglen / 2, NULL); |
285 | | /* cp += siglen / 2; */ |
286 | 0 | ECDSA_SIG_set0(ecdsasig, r, s); |
287 | |
|
288 | 0 | status = i2d_ECDSA_SIG(ecdsasig, NULL); |
289 | 0 | if (status < 0) { |
290 | 0 | CLEANUP(dst__openssl_toresult3(dctx->category, "i2d_ECDSA_SIG", |
291 | 0 | DST_R_VERIFYFAILURE)); |
292 | 0 | } |
293 | | |
294 | 0 | sigder_len = (size_t)status; |
295 | 0 | sigder = isc_mem_get(dctx->mctx, sigder_len); |
296 | 0 | sigder_alloced = sigder_len; |
297 | |
|
298 | 0 | sigder_copy = sigder; |
299 | 0 | status = i2d_ECDSA_SIG(ecdsasig, &sigder_copy); |
300 | 0 | if (status < 0) { |
301 | 0 | CLEANUP(dst__openssl_toresult3(dctx->category, "i2d_ECDSA_SIG", |
302 | 0 | DST_R_VERIFYFAILURE)); |
303 | 0 | } |
304 | | |
305 | 0 | status = EVP_DigestVerifyFinal(evp_md_ctx, sigder, sigder_len); |
306 | |
|
307 | 0 | switch (status) { |
308 | 0 | case 1: |
309 | 0 | result = ISC_R_SUCCESS; |
310 | 0 | break; |
311 | 0 | case 0: |
312 | 0 | result = dst__openssl_toresult(DST_R_VERIFYFAILURE); |
313 | 0 | break; |
314 | 0 | default: |
315 | 0 | result = dst__openssl_toresult3(dctx->category, |
316 | 0 | "EVP_DigestVerifyFinal", |
317 | 0 | DST_R_VERIFYFAILURE); |
318 | 0 | break; |
319 | 0 | } |
320 | | |
321 | 0 | cleanup: |
322 | 0 | if (ecdsasig != NULL) { |
323 | 0 | ECDSA_SIG_free(ecdsasig); |
324 | 0 | } |
325 | 0 | if (sigder != NULL && sigder_alloced != 0) { |
326 | 0 | isc_mem_put(dctx->mctx, sigder, sigder_alloced); |
327 | 0 | } |
328 | |
|
329 | 0 | return result; |
330 | 0 | } |
331 | | |
332 | | static isc_result_t |
333 | 0 | opensslecdsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { |
334 | 0 | EVP_PKEY *pkey = NULL; |
335 | |
|
336 | 0 | REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); |
337 | 0 | UNUSED(unused); |
338 | 0 | UNUSED(callback); |
339 | |
|
340 | 0 | if (key->label != NULL) { |
341 | 0 | switch (key->key_alg) { |
342 | 0 | case DST_ALG_ECDSA256: |
343 | 0 | RETERR(isc_ossl_wrap_generate_pkcs11_p256_key( |
344 | 0 | key->label, &pkey)); |
345 | 0 | break; |
346 | 0 | case DST_ALG_ECDSA384: |
347 | 0 | RETERR(isc_ossl_wrap_generate_pkcs11_p384_key( |
348 | 0 | key->label, &pkey)); |
349 | 0 | break; |
350 | 0 | default: |
351 | 0 | UNREACHABLE(); |
352 | 0 | } |
353 | 0 | } else { |
354 | 0 | switch (key->key_alg) { |
355 | 0 | case DST_ALG_ECDSA256: |
356 | 0 | RETERR(isc_ossl_wrap_generate_p256_key(&pkey)); |
357 | 0 | break; |
358 | 0 | case DST_ALG_ECDSA384: |
359 | 0 | RETERR(isc_ossl_wrap_generate_p384_key(&pkey)); |
360 | 0 | break; |
361 | 0 | default: |
362 | 0 | UNREACHABLE(); |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | 0 | key->key_size = EVP_PKEY_bits(pkey); |
367 | 0 | key->keydata.pkeypair.priv = pkey; |
368 | 0 | key->keydata.pkeypair.pub = pkey; |
369 | 0 | return ISC_R_SUCCESS; |
370 | 0 | } |
371 | | |
372 | | static isc_result_t |
373 | 0 | opensslecdsa_todns(const dst_key_t *key, isc_buffer_t *data) { |
374 | 0 | isc_result_t result; |
375 | 0 | isc_region_t r; |
376 | 0 | EVP_PKEY *pkey; |
377 | 0 | size_t keysize; |
378 | |
|
379 | 0 | REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); |
380 | 0 | REQUIRE(key->keydata.pkeypair.pub != NULL); |
381 | |
|
382 | 0 | keysize = opensslecdsa_key_alg_to_publickey_size(key->key_alg); |
383 | 0 | isc_buffer_availableregion(data, &r); |
384 | 0 | if (r.length < keysize) { |
385 | 0 | return ISC_R_NOSPACE; |
386 | 0 | } |
387 | | |
388 | 0 | pkey = key->keydata.pkeypair.pub; |
389 | 0 | switch (key->key_alg) { |
390 | 0 | case DST_ALG_ECDSA256: |
391 | 0 | if (isc_ossl_wrap_p256_public_region(pkey, r) != ISC_R_SUCCESS) |
392 | 0 | { |
393 | 0 | return dst__openssl_toresult(DST_R_OPENSSLFAILURE); |
394 | 0 | } |
395 | 0 | break; |
396 | 0 | case DST_ALG_ECDSA384: |
397 | 0 | if (isc_ossl_wrap_p384_public_region(pkey, r) != ISC_R_SUCCESS) |
398 | 0 | { |
399 | 0 | return dst__openssl_toresult(DST_R_OPENSSLFAILURE); |
400 | 0 | } |
401 | 0 | break; |
402 | 0 | default: |
403 | 0 | UNREACHABLE(); |
404 | 0 | } |
405 | | |
406 | 0 | isc_buffer_add(data, keysize); |
407 | 0 | result = ISC_R_SUCCESS; |
408 | |
|
409 | 0 | return result; |
410 | 0 | } |
411 | | |
412 | | static isc_result_t |
413 | 0 | opensslecdsa_fromdns(dst_key_t *key, isc_buffer_t *data) { |
414 | 0 | isc_result_t result; |
415 | 0 | EVP_PKEY *pkey = NULL; |
416 | 0 | isc_region_t r; |
417 | 0 | size_t len; |
418 | |
|
419 | 0 | REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); |
420 | 0 | len = opensslecdsa_key_alg_to_publickey_size(key->key_alg); |
421 | |
|
422 | 0 | isc_buffer_remainingregion(data, &r); |
423 | 0 | if (r.length == 0) { |
424 | 0 | return ISC_R_SUCCESS; |
425 | 0 | } |
426 | 0 | if (r.length != len) { |
427 | 0 | return DST_R_INVALIDPUBLICKEY; |
428 | 0 | } |
429 | | |
430 | 0 | switch (key->key_alg) { |
431 | 0 | case DST_ALG_ECDSA256: |
432 | 0 | RETERR(isc_ossl_wrap_load_p256_public_from_region(r, &pkey)); |
433 | 0 | break; |
434 | 0 | case DST_ALG_ECDSA384: |
435 | 0 | RETERR(isc_ossl_wrap_load_p384_public_from_region(r, &pkey)); |
436 | 0 | break; |
437 | 0 | default: |
438 | 0 | UNREACHABLE(); |
439 | 0 | } |
440 | | |
441 | 0 | isc_buffer_forward(data, len); |
442 | 0 | key->key_size = EVP_PKEY_bits(pkey); |
443 | 0 | key->keydata.pkeypair.pub = pkey; |
444 | 0 | result = ISC_R_SUCCESS; |
445 | |
|
446 | 0 | return result; |
447 | 0 | } |
448 | | |
449 | | static isc_result_t |
450 | 0 | opensslecdsa_tofile(const dst_key_t *key, const char *directory) { |
451 | 0 | isc_result_t result; |
452 | 0 | dst_private_t priv; |
453 | 0 | unsigned char buf[MAX_PRIVKEY_SIZE]; |
454 | 0 | size_t keylen = 0; |
455 | 0 | unsigned short i; |
456 | 0 | EVP_PKEY *pkey; |
457 | |
|
458 | 0 | if (key->keydata.pkeypair.pub == NULL) { |
459 | 0 | CLEANUP(DST_R_NULLKEY); |
460 | 0 | } |
461 | | |
462 | 0 | if (key->external) { |
463 | 0 | priv.nelements = 0; |
464 | 0 | CLEANUP(dst__privstruct_writefile(key, &priv, directory)); |
465 | 0 | } |
466 | | |
467 | 0 | if (key->keydata.pkeypair.priv == NULL) { |
468 | 0 | CLEANUP(DST_R_NULLKEY); |
469 | 0 | } |
470 | | |
471 | 0 | keylen = opensslecdsa_key_alg_to_publickey_size(key->key_alg) / 2; |
472 | 0 | INSIST(keylen <= sizeof(buf)); |
473 | |
|
474 | 0 | pkey = key->keydata.pkeypair.priv; |
475 | |
|
476 | 0 | i = 0; |
477 | 0 | switch (key->key_alg) { |
478 | 0 | case DST_ALG_ECDSA256: |
479 | 0 | result = isc_ossl_wrap_p256_secret_region( |
480 | 0 | pkey, (isc_region_t){ buf, keylen }); |
481 | 0 | break; |
482 | 0 | case DST_ALG_ECDSA384: |
483 | 0 | result = isc_ossl_wrap_p384_secret_region( |
484 | 0 | pkey, (isc_region_t){ buf, keylen }); |
485 | 0 | break; |
486 | 0 | default: |
487 | 0 | UNREACHABLE(); |
488 | 0 | } |
489 | | |
490 | 0 | if (result == ISC_R_SUCCESS) { |
491 | 0 | priv.elements[i].tag = TAG_ECDSA_PRIVATEKEY; |
492 | 0 | priv.elements[i].length = keylen; |
493 | 0 | priv.elements[i].data = buf; |
494 | 0 | i++; |
495 | 0 | } |
496 | |
|
497 | 0 | if (key->label != NULL) { |
498 | 0 | priv.elements[i].tag = TAG_ECDSA_LABEL; |
499 | 0 | priv.elements[i].length = (unsigned short)strlen(key->label) + |
500 | 0 | 1; |
501 | 0 | priv.elements[i].data = (unsigned char *)key->label; |
502 | 0 | i++; |
503 | 0 | } |
504 | |
|
505 | 0 | priv.nelements = i; |
506 | 0 | result = dst__privstruct_writefile(key, &priv, directory); |
507 | |
|
508 | 0 | cleanup: |
509 | 0 | isc_safe_memwipe(buf, keylen); |
510 | 0 | return result; |
511 | 0 | } |
512 | | |
513 | | static isc_result_t |
514 | | opensslecdsa_fromlabel(dst_key_t *key, const char *label, const char *pin); |
515 | | |
516 | | static isc_result_t |
517 | 0 | opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { |
518 | 0 | dst_private_t priv; |
519 | 0 | isc_result_t result; |
520 | 0 | isc_region_t r; |
521 | 0 | EVP_PKEY *pkey = NULL; |
522 | 0 | const char *label = NULL; |
523 | 0 | int i, privkey_index = -1; |
524 | |
|
525 | 0 | REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); |
526 | | |
527 | | /* read private key file */ |
528 | 0 | CHECK(dst__privstruct_parse(key, DST_ALG_ECDSA256, lexer, key->mctx, |
529 | 0 | &priv)); |
530 | |
|
531 | 0 | if (key->external) { |
532 | 0 | if (priv.nelements != 0 || pub == NULL) { |
533 | 0 | CLEANUP(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); |
534 | 0 | } |
535 | 0 | key->keydata.pkeypair.priv = pub->keydata.pkeypair.priv; |
536 | 0 | key->keydata.pkeypair.pub = pub->keydata.pkeypair.pub; |
537 | 0 | pub->keydata.pkeypair.priv = NULL; |
538 | 0 | pub->keydata.pkeypair.pub = NULL; |
539 | 0 | CLEANUP(ISC_R_SUCCESS); |
540 | 0 | } |
541 | | |
542 | 0 | for (i = 0; i < priv.nelements; i++) { |
543 | 0 | switch (priv.elements[i].tag) { |
544 | 0 | case TAG_ECDSA_ENGINE: |
545 | | /* The Engine: tag is explicitly ignored */ |
546 | 0 | break; |
547 | 0 | case TAG_ECDSA_LABEL: |
548 | | /* NUL terminated data? */ |
549 | 0 | CHECK(dst__privelement_is_nul_terminated( |
550 | 0 | &priv.elements[i])); |
551 | 0 | label = (char *)priv.elements[i].data; |
552 | 0 | break; |
553 | 0 | case TAG_ECDSA_PRIVATEKEY: |
554 | 0 | privkey_index = i; |
555 | 0 | break; |
556 | 0 | default: |
557 | 0 | break; |
558 | 0 | } |
559 | 0 | } |
560 | | |
561 | 0 | if (label != NULL) { |
562 | 0 | CHECK(opensslecdsa_fromlabel(key, label, NULL)); |
563 | | /* Check that the public component matches if given */ |
564 | 0 | if (pub != NULL && EVP_PKEY_eq(key->keydata.pkeypair.pub, |
565 | 0 | pub->keydata.pkeypair.pub) != 1) |
566 | 0 | { |
567 | 0 | CLEANUP(DST_R_INVALIDPRIVATEKEY); |
568 | 0 | } |
569 | 0 | CLEANUP(ISC_R_SUCCESS); |
570 | 0 | } |
571 | | |
572 | 0 | if (privkey_index < 0) { |
573 | 0 | CLEANUP(dst__openssl_toresult(DST_R_INVALIDPRIVATEKEY)); |
574 | 0 | } |
575 | | |
576 | 0 | r = (isc_region_t){ |
577 | 0 | .base = priv.elements[privkey_index].data, |
578 | 0 | .length = priv.elements[privkey_index].length, |
579 | 0 | }; |
580 | |
|
581 | 0 | switch (key->key_alg) { |
582 | 0 | case DST_ALG_ECDSA256: |
583 | 0 | CHECK(isc_ossl_wrap_load_p256_secret_from_region(r, &pkey)); |
584 | 0 | break; |
585 | 0 | case DST_ALG_ECDSA384: |
586 | 0 | CHECK(isc_ossl_wrap_load_p384_secret_from_region(r, &pkey)); |
587 | 0 | break; |
588 | 0 | default: |
589 | 0 | UNREACHABLE(); |
590 | 0 | } |
591 | | |
592 | | /* Check that the public component matches if given */ |
593 | 0 | if (pub != NULL && EVP_PKEY_eq(pkey, pub->keydata.pkeypair.pub) != 1) { |
594 | 0 | CLEANUP(DST_R_INVALIDPRIVATEKEY); |
595 | 0 | } |
596 | | |
597 | 0 | key->key_size = EVP_PKEY_bits(pkey); |
598 | 0 | key->keydata.pkeypair.priv = pkey; |
599 | 0 | key->keydata.pkeypair.pub = pkey; |
600 | 0 | pkey = NULL; |
601 | |
|
602 | 0 | cleanup: |
603 | 0 | EVP_PKEY_free(pkey); |
604 | 0 | if (result != ISC_R_SUCCESS) { |
605 | 0 | key->keydata.generic = NULL; |
606 | 0 | } |
607 | 0 | dst__privstruct_free(&priv, key->mctx); |
608 | 0 | isc_safe_memwipe(&priv, sizeof(priv)); |
609 | |
|
610 | 0 | return result; |
611 | 0 | } |
612 | | |
613 | | static isc_result_t |
614 | 0 | opensslecdsa_fromlabel(dst_key_t *key, const char *label, const char *pin) { |
615 | 0 | EVP_PKEY *privpkey = NULL, *pubpkey = NULL; |
616 | 0 | isc_result_t result; |
617 | |
|
618 | 0 | REQUIRE(opensslecdsa_valid_key_alg(key->key_alg)); |
619 | 0 | UNUSED(pin); |
620 | |
|
621 | 0 | CHECK(dst__openssl_fromlabel(EVP_PKEY_EC, label, pin, &pubpkey, |
622 | 0 | &privpkey)); |
623 | |
|
624 | 0 | switch (key->key_alg) { |
625 | 0 | case DST_ALG_ECDSA256: |
626 | 0 | CHECK(isc_ossl_wrap_validate_p256_pkey(privpkey)); |
627 | 0 | CHECK(isc_ossl_wrap_validate_p256_pkey(pubpkey)); |
628 | 0 | break; |
629 | 0 | case DST_ALG_ECDSA384: |
630 | 0 | CHECK(isc_ossl_wrap_validate_p384_pkey(privpkey)); |
631 | 0 | CHECK(isc_ossl_wrap_validate_p384_pkey(pubpkey)); |
632 | 0 | break; |
633 | 0 | default: |
634 | 0 | UNREACHABLE(); |
635 | 0 | } |
636 | | |
637 | 0 | key->label = isc_mem_strdup(key->mctx, label); |
638 | 0 | key->key_size = EVP_PKEY_bits(privpkey); |
639 | 0 | key->keydata.pkeypair.priv = privpkey; |
640 | 0 | key->keydata.pkeypair.pub = pubpkey; |
641 | 0 | privpkey = NULL; |
642 | 0 | pubpkey = NULL; |
643 | |
|
644 | 0 | cleanup: |
645 | 0 | EVP_PKEY_free(privpkey); |
646 | 0 | EVP_PKEY_free(pubpkey); |
647 | 0 | return result; |
648 | 0 | } |
649 | | |
650 | | static dst_func_t opensslecdsa_functions = { |
651 | | .createctx = opensslecdsa_createctx, |
652 | | .destroyctx = opensslecdsa_destroyctx, |
653 | | .adddata = opensslecdsa_adddata, |
654 | | .sign = opensslecdsa_sign, |
655 | | .verify = opensslecdsa_verify, |
656 | | .compare = dst__openssl_keypair_compare, |
657 | | .generate = opensslecdsa_generate, |
658 | | .isprivate = dst__openssl_keypair_isprivate, |
659 | | .destroy = dst__openssl_keypair_destroy, |
660 | | .todns = opensslecdsa_todns, |
661 | | .fromdns = opensslecdsa_fromdns, |
662 | | .tofile = opensslecdsa_tofile, |
663 | | .parse = opensslecdsa_parse, |
664 | | .fromlabel = opensslecdsa_fromlabel, |
665 | | }; |
666 | | |
667 | | void |
668 | 44 | dst__opensslecdsa_init(dst_func_t **funcp) { |
669 | 44 | REQUIRE(funcp != NULL); |
670 | | |
671 | 44 | if (*funcp == NULL) { |
672 | 44 | *funcp = &opensslecdsa_functions; |
673 | 44 | } |
674 | 44 | } |