/src/bind9/lib/dns/openssleddsa_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/err.h> |
19 | | #include <openssl/evp.h> |
20 | | #include <openssl/objects.h> |
21 | | #include <openssl/x509.h> |
22 | | |
23 | | #include <isc/crypto.h> |
24 | | #include <isc/mem.h> |
25 | | #include <isc/ossl_wrap.h> |
26 | | #include <isc/result.h> |
27 | | #include <isc/safe.h> |
28 | | #include <isc/string.h> |
29 | | #include <isc/util.h> |
30 | | |
31 | | #include <dns/keyvalues.h> |
32 | | |
33 | | #include "dst_internal.h" |
34 | | #include "dst_openssl.h" |
35 | | #include "dst_parse.h" |
36 | | #include "openssl_shim.h" |
37 | | |
38 | | #ifndef NID_ED25519 |
39 | | #error "Ed25519 group is not known (NID_ED25519)" |
40 | | #endif /* ifndef NID_ED25519 */ |
41 | | |
42 | | #if HAVE_OPENSSL_ED448 |
43 | | #ifndef NID_ED448 |
44 | | #error "Ed448 group is not known (NID_ED448)" |
45 | | #endif /* ifndef NID_ED448 */ |
46 | | #endif /* HAVE_OPENSSL_ED448 */ |
47 | | |
48 | | typedef struct eddsa_alginfo { |
49 | | int pkey_type, nid; |
50 | | unsigned int key_size, sig_size; |
51 | | } eddsa_alginfo_t; |
52 | | |
53 | | static const eddsa_alginfo_t * |
54 | 44 | openssleddsa_alg_info(unsigned int key_alg) { |
55 | 44 | if (key_alg == DST_ALG_ED25519) { |
56 | 22 | static const eddsa_alginfo_t ed25519_alginfo = { |
57 | 22 | .pkey_type = EVP_PKEY_ED25519, |
58 | 22 | .nid = NID_ED25519, |
59 | 22 | .key_size = DNS_KEY_ED25519SIZE, |
60 | 22 | .sig_size = DNS_SIG_ED25519SIZE, |
61 | 22 | }; |
62 | 22 | return &ed25519_alginfo; |
63 | 22 | } |
64 | 22 | #if HAVE_OPENSSL_ED448 |
65 | 22 | if (key_alg == DST_ALG_ED448) { |
66 | 22 | static const eddsa_alginfo_t ed448_alginfo = { |
67 | 22 | .pkey_type = EVP_PKEY_ED448, |
68 | 22 | .nid = NID_ED448, |
69 | 22 | .key_size = DNS_KEY_ED448SIZE, |
70 | 22 | .sig_size = DNS_SIG_ED448SIZE, |
71 | 22 | }; |
72 | 22 | return &ed448_alginfo; |
73 | 22 | } |
74 | 0 | #endif /* HAVE_OPENSSL_ED448 */ |
75 | 0 | return NULL; |
76 | 22 | } |
77 | | |
78 | | static isc_result_t |
79 | | raw_key_to_ossl(const eddsa_alginfo_t *alginfo, int private, |
80 | 44 | const unsigned char *key, size_t *key_len, EVP_PKEY **pkey) { |
81 | 44 | isc_result_t result; |
82 | 44 | int pkey_type = alginfo->pkey_type; |
83 | 44 | size_t len = alginfo->key_size; |
84 | | |
85 | 44 | result = (private ? DST_R_INVALIDPRIVATEKEY : DST_R_INVALIDPUBLICKEY); |
86 | 44 | if (*key_len < len) { |
87 | 0 | return result; |
88 | 0 | } |
89 | | |
90 | 44 | if (private) { |
91 | 0 | *pkey = EVP_PKEY_new_raw_private_key(pkey_type, NULL, key, len); |
92 | 44 | } else { |
93 | 44 | *pkey = EVP_PKEY_new_raw_public_key(pkey_type, NULL, key, len); |
94 | 44 | } |
95 | 44 | if (*pkey == NULL) { |
96 | 0 | return dst__openssl_toresult(result); |
97 | 0 | } |
98 | | |
99 | 44 | *key_len = len; |
100 | 44 | return ISC_R_SUCCESS; |
101 | 44 | } |
102 | | |
103 | | static isc_result_t |
104 | | openssleddsa_fromlabel(dst_key_t *key, const char *label, const char *pin); |
105 | | |
106 | | static isc_result_t |
107 | 0 | openssleddsa_createctx(dst_key_t *key, dst_context_t *dctx) { |
108 | 0 | isc_buffer_t *buf = NULL; |
109 | 0 | const eddsa_alginfo_t *alginfo = |
110 | 0 | openssleddsa_alg_info(dctx->key->key_alg); |
111 | |
|
112 | 0 | UNUSED(key); |
113 | 0 | REQUIRE(alginfo != NULL); |
114 | |
|
115 | 0 | isc_buffer_allocate(dctx->mctx, &buf, 64); |
116 | 0 | dctx->ctxdata.generic = buf; |
117 | |
|
118 | 0 | return ISC_R_SUCCESS; |
119 | 0 | } |
120 | | |
121 | | static void |
122 | 0 | openssleddsa_destroyctx(dst_context_t *dctx) { |
123 | 0 | isc_buffer_t *buf = (isc_buffer_t *)dctx->ctxdata.generic; |
124 | 0 | const eddsa_alginfo_t *alginfo = |
125 | 0 | openssleddsa_alg_info(dctx->key->key_alg); |
126 | |
|
127 | 0 | REQUIRE(alginfo != NULL); |
128 | 0 | if (buf != NULL) { |
129 | 0 | isc_buffer_free(&buf); |
130 | 0 | } |
131 | 0 | dctx->ctxdata.generic = NULL; |
132 | 0 | } |
133 | | |
134 | | static isc_result_t |
135 | 0 | openssleddsa_adddata(dst_context_t *dctx, const isc_region_t *data) { |
136 | 0 | isc_buffer_t *buf = (isc_buffer_t *)dctx->ctxdata.generic; |
137 | 0 | isc_buffer_t *nbuf = NULL; |
138 | 0 | isc_region_t r; |
139 | 0 | unsigned int length; |
140 | 0 | isc_result_t result; |
141 | 0 | const eddsa_alginfo_t *alginfo = |
142 | 0 | openssleddsa_alg_info(dctx->key->key_alg); |
143 | |
|
144 | 0 | REQUIRE(alginfo != NULL); |
145 | |
|
146 | 0 | result = isc_buffer_copyregion(buf, data); |
147 | 0 | if (result == ISC_R_SUCCESS) { |
148 | 0 | return ISC_R_SUCCESS; |
149 | 0 | } |
150 | | |
151 | 0 | length = isc_buffer_length(buf) + data->length + 64; |
152 | 0 | isc_buffer_allocate(dctx->mctx, &nbuf, length); |
153 | 0 | isc_buffer_usedregion(buf, &r); |
154 | 0 | (void)isc_buffer_copyregion(nbuf, &r); |
155 | 0 | (void)isc_buffer_copyregion(nbuf, data); |
156 | 0 | isc_buffer_free(&buf); |
157 | 0 | dctx->ctxdata.generic = nbuf; |
158 | |
|
159 | 0 | return ISC_R_SUCCESS; |
160 | 0 | } |
161 | | |
162 | | static isc_result_t |
163 | 0 | openssleddsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { |
164 | 0 | isc_result_t result; |
165 | 0 | dst_key_t *key = dctx->key; |
166 | 0 | isc_region_t tbsreg; |
167 | 0 | isc_region_t sigreg; |
168 | 0 | EVP_PKEY *pkey = key->keydata.pkeypair.priv; |
169 | 0 | EVP_MD_CTX *ctx = EVP_MD_CTX_new(); |
170 | 0 | isc_buffer_t *buf = (isc_buffer_t *)dctx->ctxdata.generic; |
171 | 0 | const eddsa_alginfo_t *alginfo = openssleddsa_alg_info(key->key_alg); |
172 | 0 | size_t siglen; |
173 | |
|
174 | 0 | REQUIRE(alginfo != NULL); |
175 | |
|
176 | 0 | if (ctx == NULL) { |
177 | 0 | return ISC_R_NOMEMORY; |
178 | 0 | } |
179 | | |
180 | 0 | siglen = alginfo->sig_size; |
181 | 0 | isc_buffer_availableregion(sig, &sigreg); |
182 | 0 | if (sigreg.length < (unsigned int)siglen) { |
183 | 0 | CLEANUP(ISC_R_NOSPACE); |
184 | 0 | } |
185 | | |
186 | 0 | isc_buffer_usedregion(buf, &tbsreg); |
187 | |
|
188 | 0 | if (EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey) != 1) { |
189 | 0 | CLEANUP(dst__openssl_toresult3( |
190 | 0 | dctx->category, "EVP_DigestSignInit", ISC_R_FAILURE)); |
191 | 0 | } |
192 | 0 | if (EVP_DigestSign(ctx, sigreg.base, &siglen, tbsreg.base, |
193 | 0 | tbsreg.length) != 1) |
194 | 0 | { |
195 | 0 | CLEANUP(dst__openssl_toresult3(dctx->category, "EVP_DigestSign", |
196 | 0 | DST_R_SIGNFAILURE)); |
197 | 0 | } |
198 | 0 | isc_buffer_add(sig, (unsigned int)siglen); |
199 | 0 | result = ISC_R_SUCCESS; |
200 | |
|
201 | 0 | cleanup: |
202 | 0 | EVP_MD_CTX_free(ctx); |
203 | 0 | isc_buffer_free(&buf); |
204 | 0 | dctx->ctxdata.generic = NULL; |
205 | |
|
206 | 0 | return result; |
207 | 0 | } |
208 | | |
209 | | static isc_result_t |
210 | 0 | openssleddsa_verify(dst_context_t *dctx, const isc_region_t *sig) { |
211 | 0 | isc_result_t result; |
212 | 0 | dst_key_t *key = dctx->key; |
213 | 0 | int status; |
214 | 0 | isc_region_t tbsreg; |
215 | 0 | EVP_PKEY *pkey = key->keydata.pkeypair.pub; |
216 | 0 | EVP_MD_CTX *ctx = EVP_MD_CTX_new(); |
217 | 0 | isc_buffer_t *buf = (isc_buffer_t *)dctx->ctxdata.generic; |
218 | 0 | const eddsa_alginfo_t *alginfo = openssleddsa_alg_info(key->key_alg); |
219 | |
|
220 | 0 | REQUIRE(alginfo != NULL); |
221 | |
|
222 | 0 | if (ctx == NULL) { |
223 | 0 | return dst__openssl_toresult(ISC_R_NOMEMORY); |
224 | 0 | } |
225 | | |
226 | 0 | if (sig->length != alginfo->sig_size) { |
227 | 0 | CLEANUP(DST_R_VERIFYFAILURE); |
228 | 0 | } |
229 | | |
230 | 0 | isc_buffer_usedregion(buf, &tbsreg); |
231 | |
|
232 | 0 | if (EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey) != 1) { |
233 | 0 | CLEANUP(dst__openssl_toresult3( |
234 | 0 | dctx->category, "EVP_DigestVerifyInit", ISC_R_FAILURE)); |
235 | 0 | } |
236 | | |
237 | 0 | status = EVP_DigestVerify(ctx, sig->base, sig->length, tbsreg.base, |
238 | 0 | tbsreg.length); |
239 | |
|
240 | 0 | switch (status) { |
241 | 0 | case 1: |
242 | 0 | result = ISC_R_SUCCESS; |
243 | 0 | break; |
244 | 0 | case 0: |
245 | 0 | result = dst__openssl_toresult(DST_R_VERIFYFAILURE); |
246 | 0 | break; |
247 | 0 | default: |
248 | 0 | result = dst__openssl_toresult3(dctx->category, |
249 | 0 | "EVP_DigestVerify", |
250 | 0 | DST_R_VERIFYFAILURE); |
251 | 0 | break; |
252 | 0 | } |
253 | | |
254 | 0 | cleanup: |
255 | 0 | EVP_MD_CTX_free(ctx); |
256 | 0 | isc_buffer_free(&buf); |
257 | 0 | dctx->ctxdata.generic = NULL; |
258 | |
|
259 | 0 | return result; |
260 | 0 | } |
261 | | |
262 | | static isc_result_t |
263 | 0 | openssleddsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { |
264 | 0 | isc_result_t result; |
265 | 0 | EVP_PKEY *pkey = NULL; |
266 | 0 | EVP_PKEY_CTX *ctx = NULL; |
267 | 0 | const eddsa_alginfo_t *alginfo = openssleddsa_alg_info(key->key_alg); |
268 | 0 | int status; |
269 | |
|
270 | 0 | REQUIRE(alginfo != NULL); |
271 | 0 | UNUSED(unused); |
272 | 0 | UNUSED(callback); |
273 | |
|
274 | 0 | if (key->label != NULL) { |
275 | 0 | switch (key->key_alg) { |
276 | 0 | case DST_ALG_ED25519: |
277 | 0 | RETERR(isc_ossl_wrap_generate_pkcs11_ed25519_key( |
278 | 0 | key->label, &pkey)); |
279 | 0 | break; |
280 | 0 | #if HAVE_OPENSSL_ED448 |
281 | 0 | case DST_ALG_ED448: |
282 | 0 | RETERR(isc_ossl_wrap_generate_pkcs11_ed448_key( |
283 | 0 | key->label, &pkey)); |
284 | 0 | break; |
285 | 0 | #endif /* HAVE_OPENSSL_ED448 */ |
286 | 0 | default: |
287 | 0 | UNREACHABLE(); |
288 | 0 | } |
289 | 0 | key->key_size = alginfo->key_size * 8; |
290 | 0 | key->keydata.pkeypair.priv = pkey; |
291 | 0 | key->keydata.pkeypair.pub = pkey; |
292 | 0 | return ISC_R_SUCCESS; |
293 | 0 | } |
294 | | |
295 | 0 | ctx = EVP_PKEY_CTX_new_id(alginfo->nid, NULL); |
296 | 0 | if (ctx == NULL) { |
297 | 0 | return dst__openssl_toresult2("EVP_PKEY_CTX_new_id", |
298 | 0 | DST_R_OPENSSLFAILURE); |
299 | 0 | } |
300 | | |
301 | 0 | status = EVP_PKEY_keygen_init(ctx); |
302 | 0 | if (status != 1) { |
303 | 0 | CLEANUP(dst__openssl_toresult2("EVP_PKEY_keygen_init", |
304 | 0 | DST_R_OPENSSLFAILURE)); |
305 | 0 | } |
306 | | |
307 | 0 | status = EVP_PKEY_keygen(ctx, &pkey); |
308 | 0 | if (status != 1) { |
309 | 0 | CLEANUP(dst__openssl_toresult2("EVP_PKEY_keygen", |
310 | 0 | DST_R_OPENSSLFAILURE)); |
311 | 0 | } |
312 | | |
313 | 0 | key->key_size = alginfo->key_size * 8; |
314 | 0 | key->keydata.pkeypair.priv = pkey; |
315 | 0 | key->keydata.pkeypair.pub = pkey; |
316 | 0 | result = ISC_R_SUCCESS; |
317 | |
|
318 | 0 | cleanup: |
319 | 0 | EVP_PKEY_CTX_free(ctx); |
320 | 0 | return result; |
321 | 0 | } |
322 | | |
323 | | static isc_result_t |
324 | 0 | openssleddsa_todns(const dst_key_t *key, isc_buffer_t *data) { |
325 | 0 | const eddsa_alginfo_t *alginfo = openssleddsa_alg_info(key->key_alg); |
326 | 0 | EVP_PKEY *pkey = key->keydata.pkeypair.pub; |
327 | 0 | isc_region_t r; |
328 | 0 | size_t len; |
329 | |
|
330 | 0 | REQUIRE(pkey != NULL); |
331 | 0 | REQUIRE(alginfo != NULL); |
332 | |
|
333 | 0 | len = alginfo->key_size; |
334 | 0 | isc_buffer_availableregion(data, &r); |
335 | 0 | if (r.length < len) { |
336 | 0 | return ISC_R_NOSPACE; |
337 | 0 | } |
338 | | |
339 | 0 | if (EVP_PKEY_get_raw_public_key(pkey, r.base, &len) != 1) { |
340 | 0 | return dst__openssl_toresult(ISC_R_FAILURE); |
341 | 0 | } |
342 | | |
343 | 0 | isc_buffer_add(data, len); |
344 | 0 | return ISC_R_SUCCESS; |
345 | 0 | } |
346 | | |
347 | | static isc_result_t |
348 | 0 | openssleddsa_fromdns(dst_key_t *key, isc_buffer_t *data) { |
349 | 0 | const eddsa_alginfo_t *alginfo = openssleddsa_alg_info(key->key_alg); |
350 | 0 | isc_region_t r; |
351 | 0 | size_t len; |
352 | 0 | EVP_PKEY *pkey = NULL; |
353 | |
|
354 | 0 | REQUIRE(alginfo != NULL); |
355 | |
|
356 | 0 | isc_buffer_remainingregion(data, &r); |
357 | 0 | if (r.length == 0) { |
358 | 0 | return ISC_R_SUCCESS; |
359 | 0 | } |
360 | | |
361 | 0 | len = r.length; |
362 | 0 | RETERR(raw_key_to_ossl(alginfo, 0, r.base, &len, &pkey)); |
363 | |
|
364 | 0 | isc_buffer_forward(data, len); |
365 | 0 | key->keydata.pkeypair.pub = pkey; |
366 | 0 | key->key_size = len * 8; |
367 | 0 | return ISC_R_SUCCESS; |
368 | 0 | } |
369 | | |
370 | | static isc_result_t |
371 | 0 | openssleddsa_tofile(const dst_key_t *key, const char *directory) { |
372 | 0 | const eddsa_alginfo_t *alginfo = openssleddsa_alg_info(key->key_alg); |
373 | 0 | isc_result_t result; |
374 | 0 | dst_private_t priv; |
375 | 0 | unsigned char *buf = NULL; |
376 | 0 | size_t len; |
377 | 0 | int i; |
378 | |
|
379 | 0 | REQUIRE(alginfo != NULL); |
380 | |
|
381 | 0 | if (key->keydata.pkeypair.pub == NULL) { |
382 | 0 | return DST_R_NULLKEY; |
383 | 0 | } |
384 | | |
385 | 0 | if (key->external) { |
386 | 0 | priv.nelements = 0; |
387 | 0 | return dst__privstruct_writefile(key, &priv, directory); |
388 | 0 | } |
389 | | |
390 | 0 | i = 0; |
391 | |
|
392 | 0 | if (dst__openssl_keypair_isprivate(key)) { |
393 | 0 | len = alginfo->key_size; |
394 | 0 | buf = isc_mem_get(key->mctx, len); |
395 | 0 | if (EVP_PKEY_get_raw_private_key(key->keydata.pkeypair.priv, |
396 | 0 | buf, &len) == 1) |
397 | 0 | { |
398 | 0 | priv.elements[i].tag = TAG_EDDSA_PRIVATEKEY; |
399 | 0 | priv.elements[i].length = len; |
400 | 0 | priv.elements[i].data = buf; |
401 | 0 | i++; |
402 | 0 | } else if (key->label != NULL) { |
403 | | /* |
404 | | * The raw private key is not extractable |
405 | | * (e.g. HSM-backed via PKCS#11); fall through to |
406 | | * writing only the label. |
407 | | */ |
408 | 0 | ERR_clear_error(); |
409 | 0 | } else { |
410 | 0 | CLEANUP(dst__openssl_toresult(DST_R_OPENSSLFAILURE)); |
411 | 0 | } |
412 | 0 | } |
413 | 0 | if (key->label != NULL) { |
414 | 0 | priv.elements[i].tag = TAG_EDDSA_LABEL; |
415 | 0 | priv.elements[i].length = (unsigned short)strlen(key->label) + |
416 | 0 | 1; |
417 | 0 | priv.elements[i].data = (unsigned char *)key->label; |
418 | 0 | i++; |
419 | 0 | } |
420 | |
|
421 | 0 | priv.nelements = i; |
422 | 0 | result = dst__privstruct_writefile(key, &priv, directory); |
423 | |
|
424 | 0 | cleanup: |
425 | 0 | if (buf != NULL) { |
426 | 0 | isc_mem_put(key->mctx, buf, alginfo->key_size); |
427 | 0 | } |
428 | 0 | return result; |
429 | 0 | } |
430 | | |
431 | | static isc_result_t |
432 | 0 | openssleddsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { |
433 | 0 | const eddsa_alginfo_t *alginfo = openssleddsa_alg_info(key->key_alg); |
434 | 0 | dst_private_t priv; |
435 | 0 | isc_result_t result; |
436 | 0 | int i, privkey_index = -1; |
437 | 0 | const char *label = NULL; |
438 | 0 | EVP_PKEY *pkey = NULL; |
439 | 0 | size_t len; |
440 | 0 | isc_mem_t *mctx = key->mctx; |
441 | |
|
442 | 0 | REQUIRE(alginfo != NULL); |
443 | | |
444 | | /* read private key file */ |
445 | 0 | CHECK(dst__privstruct_parse(key, DST_ALG_ED25519, lexer, mctx, &priv)); |
446 | |
|
447 | 0 | if (key->external) { |
448 | 0 | if (priv.nelements != 0) { |
449 | 0 | CLEANUP(DST_R_INVALIDPRIVATEKEY); |
450 | 0 | } |
451 | 0 | if (pub == NULL) { |
452 | 0 | CLEANUP(DST_R_INVALIDPRIVATEKEY); |
453 | 0 | } |
454 | 0 | key->keydata.pkeypair.priv = pub->keydata.pkeypair.priv; |
455 | 0 | key->keydata.pkeypair.pub = pub->keydata.pkeypair.pub; |
456 | 0 | pub->keydata.pkeypair.priv = NULL; |
457 | 0 | pub->keydata.pkeypair.pub = NULL; |
458 | 0 | CLEANUP(ISC_R_SUCCESS); |
459 | 0 | } |
460 | | |
461 | 0 | for (i = 0; i < priv.nelements; i++) { |
462 | 0 | switch (priv.elements[i].tag) { |
463 | 0 | case TAG_EDDSA_ENGINE: |
464 | | /* The Engine: tag is explicitly ignored */ |
465 | 0 | break; |
466 | 0 | case TAG_EDDSA_LABEL: |
467 | 0 | label = (char *)priv.elements[i].data; |
468 | 0 | break; |
469 | 0 | case TAG_EDDSA_PRIVATEKEY: |
470 | 0 | privkey_index = i; |
471 | 0 | break; |
472 | 0 | default: |
473 | 0 | break; |
474 | 0 | } |
475 | 0 | } |
476 | | |
477 | 0 | if (label != NULL) { |
478 | 0 | CHECK(openssleddsa_fromlabel(key, label, NULL)); |
479 | | /* Check that the public component matches if given */ |
480 | 0 | if (pub != NULL && EVP_PKEY_eq(key->keydata.pkeypair.pub, |
481 | 0 | pub->keydata.pkeypair.pub) != 1) |
482 | 0 | { |
483 | 0 | CLEANUP(DST_R_INVALIDPRIVATEKEY); |
484 | 0 | } |
485 | 0 | CLEANUP(ISC_R_SUCCESS); |
486 | 0 | } |
487 | | |
488 | 0 | if (privkey_index < 0) { |
489 | 0 | CLEANUP(DST_R_INVALIDPRIVATEKEY); |
490 | 0 | } |
491 | | |
492 | 0 | len = priv.elements[privkey_index].length; |
493 | 0 | CHECK(raw_key_to_ossl(alginfo, 1, priv.elements[privkey_index].data, |
494 | 0 | &len, &pkey)); |
495 | | /* Check that the public component matches if given */ |
496 | 0 | if (pub != NULL && EVP_PKEY_eq(pkey, pub->keydata.pkeypair.pub) != 1) { |
497 | 0 | CLEANUP(DST_R_INVALIDPRIVATEKEY); |
498 | 0 | } |
499 | | |
500 | 0 | key->keydata.pkeypair.priv = pkey; |
501 | 0 | key->keydata.pkeypair.pub = pkey; |
502 | 0 | key->key_size = len * 8; |
503 | 0 | pkey = NULL; |
504 | 0 | result = ISC_R_SUCCESS; |
505 | |
|
506 | 0 | cleanup: |
507 | 0 | EVP_PKEY_free(pkey); |
508 | 0 | dst__privstruct_free(&priv, mctx); |
509 | 0 | isc_safe_memwipe(&priv, sizeof(priv)); |
510 | 0 | return result; |
511 | 0 | } |
512 | | |
513 | | static isc_result_t |
514 | 0 | openssleddsa_fromlabel(dst_key_t *key, const char *label, const char *pin) { |
515 | 0 | const eddsa_alginfo_t *alginfo = openssleddsa_alg_info(key->key_alg); |
516 | 0 | EVP_PKEY *privpkey = NULL, *pubpkey = NULL; |
517 | 0 | isc_result_t result; |
518 | |
|
519 | 0 | REQUIRE(alginfo != NULL); |
520 | 0 | UNUSED(pin); |
521 | |
|
522 | 0 | CHECK(dst__openssl_fromlabel(alginfo->pkey_type, label, pin, &pubpkey, |
523 | 0 | &privpkey)); |
524 | |
|
525 | 0 | key->label = isc_mem_strdup(key->mctx, label); |
526 | 0 | key->key_size = EVP_PKEY_bits(privpkey); |
527 | 0 | key->keydata.pkeypair.priv = privpkey; |
528 | 0 | key->keydata.pkeypair.pub = pubpkey; |
529 | 0 | privpkey = NULL; |
530 | 0 | pubpkey = NULL; |
531 | |
|
532 | 0 | cleanup: |
533 | 0 | EVP_PKEY_free(privpkey); |
534 | 0 | EVP_PKEY_free(pubpkey); |
535 | 0 | return result; |
536 | 0 | } |
537 | | |
538 | | static dst_func_t openssleddsa_functions = { |
539 | | .createctx = openssleddsa_createctx, |
540 | | .destroyctx = openssleddsa_destroyctx, |
541 | | .adddata = openssleddsa_adddata, |
542 | | .sign = openssleddsa_sign, |
543 | | .verify = openssleddsa_verify, |
544 | | .compare = dst__openssl_keypair_compare, |
545 | | .generate = openssleddsa_generate, |
546 | | .isprivate = dst__openssl_keypair_isprivate, |
547 | | .destroy = dst__openssl_keypair_destroy, |
548 | | .todns = openssleddsa_todns, |
549 | | .fromdns = openssleddsa_fromdns, |
550 | | .tofile = openssleddsa_tofile, |
551 | | .parse = openssleddsa_parse, |
552 | | .fromlabel = openssleddsa_fromlabel, |
553 | | }; |
554 | | |
555 | | /* |
556 | | * The test vectors below where generated by util/gen-eddsa-vectors.c |
557 | | */ |
558 | | #if HAVE_OPENSSL_ED448 |
559 | | static unsigned char ed448_pub[] = |
560 | | "\x0a\x19\x36\xf0\x4c\x2d\xc1\xfe\xbe\xdc\xfa\xf6\xeb\xd2\x8f\x3b\x04" |
561 | | "\x14\x2e\x88\xc6\xb5\xdc\xe8\x2a\xc6\xb9\x7c\xa8\x22\xe8\x36\xfb\x06" |
562 | | "\x55\xa3\x3c\xdb\x9d\x68\x59\x7e\xa9\x5f\x93\x96\x87\x83\x28\xce\xdd" |
563 | | "\x12\xc9\xb8\x78\x02\x80"; |
564 | | static unsigned char ed448_sig[] = |
565 | | "\x7e\xec\x4e\x11\xd9\x79\x89\xd2\xe2\x85\x7a\x1c\xd7\x36\xe8\x24\x1f" |
566 | | "\x90\xa0\x9c\x84\xfb\x51\xcd\xdc\xfd\x05\xcd\x8c\x08\x51\x05\x18\xc8" |
567 | | "\x85\xb2\x28\x00\xea\xfe\x10\x46\xad\x52\xe6\xe9\x62\x35\x3b\x2a\x14" |
568 | | "\x8b\xe7\xf0\x66\x5f\x00\x66\x3c\xa1\x4d\x03\x95\xcc\x73\xfc\xf2\x40" |
569 | | "\x4b\x67\x85\x5b\x9f\xa9\x87\xb6\xbb\xa3\x9d\x73\x9f\xcb\x4e\x2c\xd2" |
570 | | "\x46\xc7\x84\xd3\x7d\x94\x32\x30\x27\xb0\xa7\xf6\x6d\xf4\x77\xe8\xf5" |
571 | | "\xb4\xee\x3f\x0e\x2b\x35\xdd\x5a\x35\xfe\x35\x00"; |
572 | | #endif |
573 | | |
574 | | static unsigned char ed25519_pub[] = |
575 | | "\x66\x5c\x21\x59\xe3\xa0\x6e\xa3\x7d\x82\x7c\xf1\xe7\xa3\xdd\xaf\xd1" |
576 | | "\x6d\x92\x81\xfb\x09\x0c\x7c\xfe\x6d\xf8\x87\x24\x7e\x6e\x25"; |
577 | | static unsigned char ed25519_sig[] = |
578 | | "\x26\x70\x5c\xc1\x85\xb6\x5e\x65\xe5\xa7\xd5\x85\x63\xc9\x1d\x45\x56" |
579 | | "\x38\xa3\x9c\xa3\x42\x4d\xc8\x89\xff\x84\xea\x2c\xa8\x8b\xfa\x2f\xab" |
580 | | "\x75\x7c\x68\x95\xfd\xdf\x62\x60\x4e\x4d\x10\xf8\x3c\xae\xcf\x18\x93" |
581 | | "\x90\x05\xa4\x54\x38\x45\x2f\x81\x71\x1e\x0f\x46\x04"; |
582 | | |
583 | | static isc_result_t |
584 | 44 | check_algorithm(unsigned char algorithm) { |
585 | 44 | EVP_MD_CTX *evp_md_ctx = EVP_MD_CTX_create(); |
586 | 44 | EVP_PKEY *pkey = NULL; |
587 | 44 | const eddsa_alginfo_t *alginfo = NULL; |
588 | 44 | const unsigned char *key = NULL; |
589 | 44 | const unsigned char *sig = NULL; |
590 | 44 | const unsigned char test[] = "test"; |
591 | 44 | isc_result_t result = ISC_R_SUCCESS; |
592 | 44 | size_t key_len, sig_len; |
593 | | |
594 | 44 | if (evp_md_ctx == NULL) { |
595 | 0 | CLEANUP(ISC_R_NOMEMORY); |
596 | 0 | } |
597 | | |
598 | 44 | switch (algorithm) { |
599 | 0 | #if HAVE_OPENSSL_ED448 |
600 | 22 | case DST_ALG_ED448: |
601 | 22 | sig = ed448_sig; |
602 | 22 | sig_len = sizeof(ed448_sig) - 1; |
603 | 22 | key = ed448_pub; |
604 | 22 | key_len = sizeof(ed448_pub) - 1; |
605 | 22 | alginfo = openssleddsa_alg_info(algorithm); |
606 | 22 | break; |
607 | 0 | #endif /* HAVE_OPENSSL_ED448 */ |
608 | 22 | case DST_ALG_ED25519: |
609 | 22 | sig = ed25519_sig; |
610 | 22 | sig_len = sizeof(ed25519_sig) - 1; |
611 | 22 | key = ed25519_pub; |
612 | 22 | key_len = sizeof(ed25519_pub) - 1; |
613 | 22 | alginfo = openssleddsa_alg_info(algorithm); |
614 | 22 | break; |
615 | 0 | default: |
616 | 0 | CLEANUP(ISC_R_NOTIMPLEMENTED); |
617 | 44 | } |
618 | | |
619 | 44 | INSIST(alginfo != NULL); |
620 | 44 | CHECK(raw_key_to_ossl(alginfo, 0, key, &key_len, &pkey)); |
621 | | |
622 | | /* |
623 | | * Check that we can verify the signature. |
624 | | */ |
625 | 44 | if (EVP_DigestVerifyInit(evp_md_ctx, NULL, NULL, NULL, pkey) != 1 || |
626 | 44 | EVP_DigestVerify(evp_md_ctx, sig, sig_len, test, |
627 | 44 | sizeof(test) - 1) != 1) |
628 | 0 | { |
629 | 0 | CLEANUP(ISC_R_NOTIMPLEMENTED); |
630 | 0 | } |
631 | | |
632 | 44 | cleanup: |
633 | 44 | if (pkey != NULL) { |
634 | 44 | EVP_PKEY_free(pkey); |
635 | 44 | } |
636 | 44 | if (evp_md_ctx != NULL) { |
637 | 44 | EVP_MD_CTX_destroy(evp_md_ctx); |
638 | 44 | } |
639 | 44 | ERR_clear_error(); |
640 | 44 | return result; |
641 | 44 | } |
642 | | |
643 | | void |
644 | 44 | dst__openssleddsa_init(dst_func_t **funcp, unsigned char algorithm) { |
645 | 44 | REQUIRE(funcp != NULL); |
646 | | |
647 | 44 | if (*funcp == NULL) { |
648 | 44 | if (check_algorithm(algorithm) == ISC_R_SUCCESS) { |
649 | 44 | *funcp = &openssleddsa_functions; |
650 | 44 | } |
651 | 44 | } |
652 | 44 | } |