diff --git a/bin/tests/system/eddsa/clean.sh b/bin/tests/system/eddsa/clean.sh new file mode 100644 index 0000000000..1c0eac79c1 --- /dev/null +++ b/bin/tests/system/eddsa/clean.sh @@ -0,0 +1,15 @@ +#!/bin/sh +# +# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +rm -f */K* */dsset-* */*.signed */trusted.conf +rm -f ns1/root.db +rm -f ns*/signer.err +rm -f dig.out* +rm -f */named.run +rm -f */named.memstats +rm -f ns*/named.lock diff --git a/bin/tests/system/eddsa/ns1/named.conf b/bin/tests/system/eddsa/ns1/named.conf new file mode 100644 index 0000000000..80c4ea3941 --- /dev/null +++ b/bin/tests/system/eddsa/ns1/named.conf @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* $Id$ */ + +// NS1 + +controls { /* empty */ }; + +options { + query-source address 10.53.0.1; + notify-source 10.53.0.1; + transfer-source 10.53.0.1; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.1; }; + listen-on-v6 { none; }; + recursion no; + notify yes; + dnssec-enable yes; + dnssec-validation yes; +}; + +zone "." { + type master; + file "root.db.signed"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/eddsa/ns1/root.db.in b/bin/tests/system/eddsa/ns1/root.db.in new file mode 100644 index 0000000000..994113cec2 --- /dev/null +++ b/bin/tests/system/eddsa/ns1/root.db.in @@ -0,0 +1,18 @@ +; Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +; $Id$ + +$TTL 300 +. IN SOA fdupont.isc.org. a.root.servers.nil. ( + 2012040600 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 600 ; minimum + ) +. NS a.root-servers.nil. +a.root-servers.nil. A 10.53.0.1 diff --git a/bin/tests/system/eddsa/ns1/sign.sh b/bin/tests/system/eddsa/ns1/sign.sh new file mode 100644 index 0000000000..9d48efc4c3 --- /dev/null +++ b/bin/tests/system/eddsa/ns1/sign.sh @@ -0,0 +1,38 @@ +#!/bin/sh -e +# +# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SYSTEMTESTTOP=../.. +. $SYSTEMTESTTOP/conf.sh + +zone=. +infile=root.db.in +zonefile=root.db + +key1=`$KEYGEN -q -r $RANDFILE -a ED25519 -n zone $zone` +key2=`$KEYGEN -q -r $RANDFILE -a ED25519 -n zone -f KSK $zone` +#key2=`$KEYGEN -q -r $RANDFILE -a ED448 -n zone -f KSK $zone` +$DSFROMKEY -a sha-256 $key2.key > dsset-256 + +cat $infile $key1.key $key2.key > $zonefile + +$SIGNER -P -g -r $RANDFILE -o $zone $zonefile > /dev/null 2> signer.err || cat signer.err + +# Configure the resolving server with a trusted key. + +cat $key1.key | grep -v '^; ' | $PERL -n -e ' +local ($dn, $class, $type, $flags, $proto, $alg, @rest) = split; +local $key = join("", @rest); +print < trusted.conf +cp trusted.conf ../ns2/trusted.conf + +cd ../ns2 && $SHELL sign.sh diff --git a/bin/tests/system/eddsa/ns2/Xexample.com.+015+03613.key b/bin/tests/system/eddsa/ns2/Xexample.com.+015+03613.key new file mode 100644 index 0000000000..ff6d5bfb73 --- /dev/null +++ b/bin/tests/system/eddsa/ns2/Xexample.com.+015+03613.key @@ -0,0 +1 @@ +example.com. IN DNSKEY 257 3 15 l02Woi0iS8Aa25FQkUd9RMzZHJpBoRQwAQEX1SxZJA4= diff --git a/bin/tests/system/eddsa/ns2/Xexample.com.+015+03613.private b/bin/tests/system/eddsa/ns2/Xexample.com.+015+03613.private new file mode 100644 index 0000000000..788b2d7d94 --- /dev/null +++ b/bin/tests/system/eddsa/ns2/Xexample.com.+015+03613.private @@ -0,0 +1,4 @@ +Private-key-format: v1.2 +Algorithm: 15 (ED25519) +PrivateKey: ODIyNjAzODQ2MjgwODAxMjI2NDUxOTAyMDQxNDIyNjI= + diff --git a/bin/tests/system/eddsa/ns2/Xexample.com.+015+35217.key b/bin/tests/system/eddsa/ns2/Xexample.com.+015+35217.key new file mode 100644 index 0000000000..71e46207e2 --- /dev/null +++ b/bin/tests/system/eddsa/ns2/Xexample.com.+015+35217.key @@ -0,0 +1 @@ +example.com. IN DNSKEY 257 3 15 zPnZ/QwEe7S8C5SPz2OfS5RR40ATk2/rYnE9xHIEijs= diff --git a/bin/tests/system/eddsa/ns2/Xexample.com.+015+35217.private b/bin/tests/system/eddsa/ns2/Xexample.com.+015+35217.private new file mode 100644 index 0000000000..78ec36df83 --- /dev/null +++ b/bin/tests/system/eddsa/ns2/Xexample.com.+015+35217.private @@ -0,0 +1,3 @@ +Private-key-format: v1.2 +Algorithm: 15 (ED25519) +PrivateKey: DSSF3o0s0f+ElWzj9E/Osxw8hLpk55chkmx0LYN5WiY= diff --git a/bin/tests/system/eddsa/ns2/example.com.db b/bin/tests/system/eddsa/ns2/example.com.db new file mode 100644 index 0000000000..e9d53038bc --- /dev/null +++ b/bin/tests/system/eddsa/ns2/example.com.db @@ -0,0 +1,22 @@ +; Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +; +; This Source Code Form is subject to the terms of the Mozilla Public +; License, v. 2.0. If a copy of the MPL was not distributed with this +; file, You can obtain one at http://mozilla.org/MPL/2.0/. + +; $Id$ + +$TTL 3600 +@ IN SOA fdupont.isc.org. ns.example.com. ( + 2012040600 ; serial + 600 ; refresh + 600 ; retry + 1200 ; expire + 3600 ; minimum + ) + MX 10 mail.example.com. + NS ns.example.com. +ns.example.com. A 10.53.0.3 +; +$INCLUDE Kexample.com.+015+03613.key +$INCLUDE Kexample.com.+015+35217.key diff --git a/bin/tests/system/eddsa/ns2/named.conf b/bin/tests/system/eddsa/ns2/named.conf new file mode 100644 index 0000000000..b4e911efe9 --- /dev/null +++ b/bin/tests/system/eddsa/ns2/named.conf @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* $Id$ */ + +// NS2 + +controls { /* empty */ }; + +options { + query-source address 10.53.0.2; + notify-source 10.53.0.2; + transfer-source 10.53.0.2; + port 5300; + pid-file "named.pid"; + listen-on { 10.53.0.2; }; + listen-on-v6 { none; }; + recursion yes; + notify yes; + dnssec-enable yes; + dnssec-validation yes; +}; + +zone "." { + type hint; + file "../../common/root.hint"; +}; + +include "trusted.conf"; diff --git a/bin/tests/system/eddsa/ns2/sign.sh b/bin/tests/system/eddsa/ns2/sign.sh new file mode 100644 index 0000000000..19e861a07c --- /dev/null +++ b/bin/tests/system/eddsa/ns2/sign.sh @@ -0,0 +1,23 @@ +#!/bin/sh -e +# +# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SYSTEMTESTTOP=../.. +. $SYSTEMTESTTOP/conf.sh + +zone=example.com. +zonefile=example.com.db +starttime=20150729220000 +endtime=20150819220000 + +for i in Xexample.com.+015+03613.key Xexample.com.+015+03613.private \ + Xexample.com.+015+35217.key Xexample.com.+015+35217.private +do + cp $i `echo $i | sed s/X/K/` +done + +$SIGNER -P -z -s $starttime -e $endtime -r $RANDFILE -o $zone $zonefile > /dev/null 2> signer.err || cat signer.err diff --git a/bin/tests/system/eddsa/prereq.sh b/bin/tests/system/eddsa/prereq.sh new file mode 100644 index 0000000000..c67a067e71 --- /dev/null +++ b/bin/tests/system/eddsa/prereq.sh @@ -0,0 +1,12 @@ +#!/bin/sh -e +# +# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +exec $SHELL ../testcrypto.sh eddsa diff --git a/bin/tests/system/eddsa/setup.sh b/bin/tests/system/eddsa/setup.sh new file mode 100644 index 0000000000..74e3e7a1da --- /dev/null +++ b/bin/tests/system/eddsa/setup.sh @@ -0,0 +1,14 @@ +#!/bin/sh -e +# +# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +test -r $RANDFILE || $GENRANDOM 800 $RANDFILE + +cd ns1 && $SHELL sign.sh diff --git a/bin/tests/system/eddsa/tests.sh b/bin/tests/system/eddsa/tests.sh new file mode 100644 index 0000000000..fefbff3aa3 --- /dev/null +++ b/bin/tests/system/eddsa/tests.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# +# Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# $Id$ + +SYSTEMTESTTOP=.. +. $SYSTEMTESTTOP/conf.sh + +status=0 +n=0 + +rm -f dig.out.* + +DIGOPTS="+tcp +noau +noadd +nosea +nostat +nocmd +dnssec -p 5300" + +# Check the example. domain + +echo "I:checking that positive validation works ($n)" +ret=0 +$DIG $DIGOPTS . @10.53.0.1 soa > dig.out.ns1.test$n || ret=1 +$DIG $DIGOPTS . @10.53.0.2 soa > dig.out.ns2.test$n || ret=1 +$PERL ../digcomp.pl dig.out.ns1.test$n dig.out.ns2.test$n || ret=1 +grep "flags:.*ad.*QUERY" dig.out.ns2.test$n > /dev/null || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +# Check test vectors (RFC 8080 + errata) + +echo "I:checking that test vectors match ($n)" +ret=0 +grep 'oL9krJun7xfBOIWcGHi7mag5/hdZrKWw15jP' ns2/example.com.db.signed > /dev/null || ret=1 +grep 'VrbpMngwcrqNAg==' ns2/example.com.db.signed > /dev/null || ret=1 +grep 'zXQ0bkYgQTEFyfLyi9QoiY6D8ZdYo4wyUhVi' ns2/example.com.db.signed > /dev/null || ret=1 +grep 'R0O7KuI5k2pcBg==' ns2/example.com.db.signed > /dev/null || ret=1 +n=`expr $n + 1` +if [ $ret != 0 ]; then echo "I:failed"; fi +status=`expr $status + $ret` + +echo "I:exit status: $status" +[ $status -eq 0 ] || exit 1 diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c index ffb980363e..c0d1920639 100644 --- a/lib/dns/dst_parse.c +++ b/lib/dns/dst_parse.c @@ -113,6 +113,10 @@ static struct parse_map map[] = { {TAG_EDDSA_PRIVATEKEY, "PrivateKey:"}, + {TAG_EDDSA_PRIVATEKEY, "PrivateKey:"}, + {TAG_EDDSA_ENGINE, "Engine:" }, + {TAG_EDDSA_LABEL, "Label:" }, + {TAG_HMACMD5_KEY, "Key:"}, {TAG_HMACMD5_BITS, "Bits:"}, @@ -285,15 +289,34 @@ check_ecdsa(const dst_private_t *priv, isc_boolean_t external) { static int check_eddsa(const dst_private_t *priv, isc_boolean_t external) { + int i, j; + isc_boolean_t have[EDDSA_NTAGS]; + isc_boolean_t ok; + unsigned int mask; if (external) return ((priv->nelements == 0) ? 0 : -1); - if (priv->nelements != EDDSA_NTAGS) - return (-1); - if (priv->elements[0].tag != TAG(DST_ALG_ED25519, 0)) - return (-1); - return (0); + for (i = 0; i < EDDSA_NTAGS; i++) + have[i] = ISC_FALSE; + for (j = 0; j < priv->nelements; j++) { + for (i = 0; i < EDDSA_NTAGS; i++) + if (priv->elements[j].tag == TAG(DST_ALG_ED25519, i)) + break; + if (i == EDDSA_NTAGS) + return (-1); + have[i] = ISC_TRUE; + } + + mask = ~0; + mask <<= sizeof(mask) * 8 - TAG_SHIFT; + mask >>= sizeof(mask) * 8 - TAG_SHIFT; + + if (have[TAG_EDDSA_ENGINE & mask]) + ok = have[TAG_EDDSA_LABEL & mask]; + else + ok = have[TAG_EDDSA_PRIVATEKEY & mask]; + return (ok ? 0 : -1 ); } static int diff --git a/lib/dns/dst_parse.h b/lib/dns/dst_parse.h index b9e3cbe52c..c92b848483 100644 --- a/lib/dns/dst_parse.h +++ b/lib/dns/dst_parse.h @@ -86,6 +86,8 @@ #define EDDSA_NTAGS 4 #define TAG_EDDSA_PRIVATEKEY ((DST_ALG_ED25519 << TAG_SHIFT) + 0) +#define TAG_EDDSA_ENGINE ((DST_ALG_ED25519 << TAG_SHIFT) + 1) +#define TAG_EDDSA_LABEL ((DST_ALG_ED25519 << TAG_SHIFT) + 2) #define OLD_HMACMD5_NTAGS 1 #define HMACMD5_NTAGS 2 diff --git a/lib/dns/openssleddsa_link.c b/lib/dns/openssleddsa_link.c new file mode 100644 index 0000000000..de3c390ff1 --- /dev/null +++ b/lib/dns/openssleddsa_link.c @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#if defined(OPENSSL) && \ + (defined(HAVE_OPENSSL_ED25519) || defined(HAVE_OPENSSL_ED448)) + +#include +#include +#include +#include +#include + +#include +#include + +#include "dst_internal.h" +#include "dst_openssl.h" +#include "dst_parse.h" + +#include +#include +#include +#include + +#ifndef NID_ED25519 +#error "Ed25519 group is not known (NID_ED25519)" +#endif +#ifndef NID_ED448 +#error "Ed448 group is not known (NID_ED448)" +#endif + +#define DST_RET(a) {ret = a; goto err;} + +/* OpenSSL doesn't provide direct access to key values */ + +#define PUBPREFIXLEN 12 + +static const unsigned char ed25519_pub_prefix[] = { + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, + 0x70, 0x03, 0x21, 0x00 +}; + +static EVP_PKEY *pub_ed25519_to_ossl(const unsigned char *key) +{ + unsigned char buf[PUBPREFIXLEN + DNS_KEY_ED25519SIZE]; + const unsigned char *p; + + memmove(buf, ed25519_pub_prefix, PUBPREFIXLEN); + memmove(buf + PUBPREFIXLEN, key, DNS_KEY_ED25519SIZE); + p = buf; + return (d2i_PUBKEY(NULL, &p, PUBPREFIXLEN + DNS_KEY_ED25519SIZE)); +} + +static isc_result_t pub_ed25519_from_ossl(EVP_PKEY *pkey, + unsigned char *key) +{ + unsigned char buf[PUBPREFIXLEN + DNS_KEY_ED25519SIZE]; + unsigned char *p; + int len; + + len = i2d_PUBKEY(pkey, NULL); + if ((len <= DNS_KEY_ED25519SIZE) || + (len > PUBPREFIXLEN + DNS_KEY_ED25519SIZE)) + return (DST_R_OPENSSLFAILURE); + p = buf; + len = i2d_PUBKEY(pkey, &p); + if ((len <= DNS_KEY_ED25519SIZE) || + (len > PUBPREFIXLEN + DNS_KEY_ED25519SIZE)) + return (DST_R_OPENSSLFAILURE); + memmove(key, buf + len - DNS_KEY_ED25519SIZE, DNS_KEY_ED25519SIZE); + return (ISC_R_SUCCESS); +} + +static const unsigned char ed448_pub_prefix[] = { + 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, + 0x71, 0x03, 0x21, 0x00 +}; + +static EVP_PKEY *pub_ed448_to_ossl(const unsigned char *key) +{ + unsigned char buf[PUBPREFIXLEN + DNS_KEY_ED448SIZE]; + const unsigned char *p; + + memmove(buf, ed448_pub_prefix, PUBPREFIXLEN); + memmove(buf + PUBPREFIXLEN, key, DNS_KEY_ED448SIZE); + p = buf; + return (d2i_PUBKEY(NULL, &p, PUBPREFIXLEN + DNS_KEY_ED448SIZE)); +} + +static isc_result_t pub_ed448_from_ossl(EVP_PKEY *pkey, + unsigned char *key) +{ + unsigned char buf[PUBPREFIXLEN + DNS_KEY_ED448SIZE]; + unsigned char *p; + int len; + + len = i2d_PUBKEY(pkey, NULL); + if ((len <= DNS_KEY_ED448SIZE) || + (len > PUBPREFIXLEN + DNS_KEY_ED448SIZE)) + return (DST_R_OPENSSLFAILURE); + p = buf; + len = i2d_PUBKEY(pkey, &p); + if ((len <= DNS_KEY_ED448SIZE) || + (len > PUBPREFIXLEN + DNS_KEY_ED448SIZE)) + return (DST_R_OPENSSLFAILURE); + memmove(key, buf + len - DNS_KEY_ED448SIZE, DNS_KEY_ED448SIZE); + return (ISC_R_SUCCESS); +} + +#define PRIVPREFIXLEN 16 + +static const unsigned char ed25519_priv_prefix[] = { + 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, + 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20 +}; + +static EVP_PKEY *priv_ed25519_to_ossl(const unsigned char *key) +{ + unsigned char buf[PRIVPREFIXLEN + DNS_KEY_ED25519SIZE]; + const unsigned char *p; + + memmove(buf, ed25519_priv_prefix, PRIVPREFIXLEN); + memmove(buf + PRIVPREFIXLEN, key, DNS_KEY_ED25519SIZE); + p = buf; + return (d2i_PrivateKey(NID_ED25519, NULL, &p, + PRIVPREFIXLEN + DNS_KEY_ED25519SIZE)); +} + +static isc_result_t priv_ed25519_from_ossl(EVP_PKEY *pkey, + unsigned char *key) +{ + unsigned char buf[PRIVPREFIXLEN + DNS_KEY_ED25519SIZE]; + unsigned char *p; + int len; + + len = i2d_PrivateKey(pkey, NULL); + if ((len <= DNS_KEY_ED25519SIZE) || + (len > PRIVPREFIXLEN + DNS_KEY_ED25519SIZE)) + return (DST_R_OPENSSLFAILURE); + p = buf; + len = i2d_PrivateKey(pkey, &p); + if ((len <= DNS_KEY_ED25519SIZE) || + (len > PRIVPREFIXLEN + DNS_KEY_ED25519SIZE)) + return (DST_R_OPENSSLFAILURE); + memmove(key, buf + len - DNS_KEY_ED25519SIZE, DNS_KEY_ED25519SIZE); + return (ISC_R_SUCCESS); +} + +static const unsigned char ed448_priv_prefix[] = { + 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, + 0x03, 0x2b, 0x65, 0x71, 0x04, 0x22, 0x04, 0x20 +}; + +static EVP_PKEY *priv_ed448_to_ossl(const unsigned char *key) +{ + unsigned char buf[PRIVPREFIXLEN + DNS_KEY_ED448SIZE]; + const unsigned char *p; + + memmove(buf, ed448_priv_prefix, PRIVPREFIXLEN); + memmove(buf + PRIVPREFIXLEN, key, DNS_KEY_ED448SIZE); + p = buf; + return (d2i_PrivateKey(NID_ED448, NULL, &p, + PRIVPREFIXLEN + DNS_KEY_ED448SIZE)); +} + +static isc_result_t priv_ed448_from_ossl(EVP_PKEY *pkey, + unsigned char *key) +{ + unsigned char buf[PRIVPREFIXLEN + DNS_KEY_ED448SIZE]; + unsigned char *p; + int len; + + len = i2d_PrivateKey(pkey, NULL); + if ((len <= DNS_KEY_ED448SIZE) || + (len > PRIVPREFIXLEN + DNS_KEY_ED448SIZE)) + return (DST_R_OPENSSLFAILURE); + p = buf; + len = i2d_PrivateKey(pkey, &p); + if ((len <= DNS_KEY_ED448SIZE) || + (len > PRIVPREFIXLEN + DNS_KEY_ED448SIZE)) + return (DST_R_OPENSSLFAILURE); + memmove(key, buf + len - DNS_KEY_ED448SIZE, DNS_KEY_ED448SIZE); + return (ISC_R_SUCCESS); +} + +static isc_result_t openssleddsa_todns(const dst_key_t *key, + isc_buffer_t *data); + +static isc_result_t +openssleddsa_createctx(dst_key_t *key, dst_context_t *dctx) { + isc_buffer_t *buf = NULL; + isc_result_t result; + + UNUSED(key); + REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || + dctx->key->key_alg == DST_ALG_ED448); + + result = isc_buffer_allocate(dctx->mctx, &buf, 64); + dctx->ctxdata.generic = buf; + + return (result); +} + +static void +openssleddsa_destroyctx(dst_context_t *dctx) { + isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic; + + REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || + dctx->key->key_alg == DST_ALG_ED448); + if (buf != NULL) + isc_buffer_free(&buf); + dctx->ctxdata.generic = NULL; +} + +static isc_result_t +openssleddsa_adddata(dst_context_t *dctx, const isc_region_t *data) { + isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic; + isc_buffer_t *nbuf = NULL; + isc_region_t r; + unsigned int length; + isc_result_t result; + + REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || + dctx->key->key_alg == DST_ALG_ED448); + + result = isc_buffer_copyregion(buf, data); + if (result == ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + + length = isc_buffer_length(buf) + data->length + 64; + result = isc_buffer_allocate(dctx->mctx, &nbuf, length); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_usedregion(buf, &r); + (void) isc_buffer_copyregion(nbuf, &r); + (void) isc_buffer_copyregion(nbuf, data); + isc_buffer_free(&buf); + dctx->ctxdata.generic = nbuf; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +openssleddsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { + isc_result_t ret; + dst_key_t *key = dctx->key; + isc_region_t tbsreg; + isc_region_t sigreg; + EVP_PKEY *pkey = key->keydata.pkey; + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic; + size_t siglen; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + + if (ctx == NULL) + return (ISC_R_NOMEMORY); + + if (key->key_alg == DST_ALG_ED25519) + siglen = DNS_SIG_ED25519SIZE; + else + siglen = DNS_SIG_ED448SIZE; + + isc_buffer_availableregion(sig, &sigreg); + if (sigreg.length < (unsigned int) siglen) + DST_RET(ISC_R_NOSPACE); + + isc_buffer_usedregion(buf, &tbsreg); + + if (!EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey)) + DST_RET(dst__openssl_toresult3(dctx->category, + "EVP_DigestSignInit", + ISC_R_FAILURE)); + if (!EVP_DigestSign(ctx, sigreg.base, &siglen, + tbsreg.base, tbsreg.length)) + DST_RET(dst__openssl_toresult3(dctx->category, + "EVP_DigestSign", + DST_R_SIGNFAILURE)); + isc_buffer_add(sig, (unsigned int) siglen); + ret = ISC_R_SUCCESS; + + err: + if (ctx != NULL) + EVP_MD_CTX_free(ctx); + isc_buffer_free(&buf); + dctx->ctxdata.generic = NULL; + + return (ret); +} + +static isc_result_t +openssleddsa_verify(dst_context_t *dctx, const isc_region_t *sig) { + isc_result_t ret; + dst_key_t *key = dctx->key; + int status; + isc_region_t tbsreg; + EVP_PKEY *pkey = key->keydata.pkey; + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic; + unsigned int siglen; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + + if (ctx == NULL) + return (ISC_R_NOMEMORY); + + if (key->key_alg == DST_ALG_ED25519) + siglen = DNS_SIG_ED25519SIZE; + else + siglen = DNS_SIG_ED448SIZE; + + if (sig->length != siglen) + return (DST_R_VERIFYFAILURE); + + isc_buffer_usedregion(buf, &tbsreg); + + if (!EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey)) + DST_RET(dst__openssl_toresult3(dctx->category, + "EVP_DigestVerifyInit", + ISC_R_FAILURE)); + + status = EVP_DigestVerify(ctx, sig->base, siglen, + tbsreg.base, tbsreg.length); + + switch (status) { + case 1: + ret = ISC_R_SUCCESS; + break; + case 0: + ret = dst__openssl_toresult(DST_R_VERIFYFAILURE); + break; + default: + ret = dst__openssl_toresult3(dctx->category, + "EVP_DigestVerify", + DST_R_VERIFYFAILURE); + break; + } + + err: + if (ctx != NULL) + EVP_MD_CTX_free(ctx); + isc_buffer_free(&buf); + dctx->ctxdata.generic = NULL; + + return (ret); +} + +static isc_boolean_t +openssleddsa_compare(const dst_key_t *key1, const dst_key_t *key2) { + int status; + EVP_PKEY *pkey1 = key1->keydata.pkey; + EVP_PKEY *pkey2 = key2->keydata.pkey; + + if (pkey1 == NULL && pkey2 == NULL) + return (ISC_TRUE); + else if (pkey1 == NULL || pkey2 == NULL) + return (ISC_FALSE); + + status = EVP_PKEY_cmp(pkey1, pkey2); + if (status == 1) + return (ISC_TRUE); + return (ISC_FALSE); +} + +static isc_result_t +openssleddsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { + isc_result_t ret; + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *ctx = NULL; + int nid, status; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + UNUSED(unused); + UNUSED(callback); + + if (key->key_alg == DST_ALG_ED25519) { + nid = NID_ED25519; + key->key_size = DNS_KEY_ED25519SIZE; + } else { + nid = NID_ED448; + key->key_size = DNS_KEY_ED448SIZE; + } + + ctx = EVP_PKEY_CTX_new_id(nid, NULL); + if (ctx == NULL) + return (dst__openssl_toresult2("EVP_PKEY_CTX_new_id", + DST_R_OPENSSLFAILURE)); + + status = EVP_PKEY_keygen_init(ctx); + if (status != 1) + DST_RET (dst__openssl_toresult2("EVP_PKEY_keygen_init", + DST_R_OPENSSLFAILURE)); + + status = EVP_PKEY_keygen(ctx, &pkey); + if (status != 1) + DST_RET (dst__openssl_toresult2("EVP_PKEY_keygen", + DST_R_OPENSSLFAILURE)); + + key->keydata.pkey = pkey; + ret = ISC_R_SUCCESS; + + err: + if (ctx != NULL) + EVP_PKEY_CTX_free(ctx); + return (ret); +} + +static isc_boolean_t +openssleddsa_isprivate(const dst_key_t *key) { + EVP_PKEY *pkey = key->keydata.pkey; + int len; + unsigned long err; + + if (pkey == NULL) + return (ISC_FALSE); + + len = i2d_PrivateKey(pkey, NULL); + if (len > 0) + return (ISC_TRUE); + /* can check if first error is EC_R_INVALID_PRIVATE_KEY */ + while ((err = ERR_get_error()) != 0) + /**/; + + return (ISC_FALSE); +} + +static void +openssleddsa_destroy(dst_key_t *key) { + EVP_PKEY *pkey = key->keydata.pkey; + + EVP_PKEY_free(pkey); + key->keydata.pkey = NULL; +} + +static isc_result_t +openssleddsa_todns(const dst_key_t *key, isc_buffer_t *data) { + EVP_PKEY *pkey = key->keydata.pkey; + isc_region_t r; + isc_result_t result; + + REQUIRE(pkey != NULL); + + pkey = key->keydata.pkey; + switch (key->key_alg) { + case DST_ALG_ED25519: + isc_buffer_availableregion(data, &r); + if (r.length < DNS_KEY_ED25519SIZE) + return (ISC_R_NOSPACE); + result = pub_ed25519_from_ossl(pkey, r.base); + if (result == ISC_R_SUCCESS) + isc_buffer_add(data, DNS_KEY_ED25519SIZE); + return (result); + case DST_ALG_ED448: + isc_buffer_availableregion(data, &r); + if (r.length < DNS_KEY_ED448SIZE) + return (ISC_R_NOSPACE); + result = pub_ed448_from_ossl(pkey, r.base); + if (result == ISC_R_SUCCESS) + isc_buffer_add(data, DNS_KEY_ED448SIZE); + return (result); + default: + INSIST(0); + } +} + +static isc_result_t +openssleddsa_fromdns(dst_key_t *key, isc_buffer_t *data) { + EVP_PKEY *pkey; + isc_region_t r; + unsigned int len; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) + return (ISC_R_SUCCESS); + if (key->key_alg == DST_ALG_ED25519) { + len = DNS_KEY_ED25519SIZE; + if (r.length < len) + return (DST_R_INVALIDPUBLICKEY); + pkey = pub_ed25519_to_ossl(r.base); + } else { + len = DNS_KEY_ED448SIZE; + if (r.length < len) + return (DST_R_INVALIDPUBLICKEY); + pkey = pub_ed448_to_ossl(r.base); + } + if (pkey == NULL) + return (dst__openssl_toresult(ISC_R_FAILURE)); + isc_buffer_forward(data, len); + key->keydata.pkey = pkey; + key->key_size = len; + return (ISC_R_SUCCESS); +} + +static isc_result_t +openssleddsa_tofile(const dst_key_t *key, const char *directory) { + isc_result_t ret; + EVP_PKEY *pkey; + dst_private_t priv; + unsigned char *buf = NULL; + unsigned int len; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + + if (key->keydata.pkey == NULL) + return (DST_R_NULLKEY); + + if (key->external) { + priv.nelements = 0; + return (dst__privstruct_writefile(key, &priv, directory)); + } + + pkey = key->keydata.pkey; + if (key->key_alg == DST_ALG_ED25519) { + len = DNS_KEY_ED25519SIZE; + buf = isc_mem_get(key->mctx, len); + if (buf == NULL) + return (ISC_R_NOMEMORY); + priv.elements[0].tag = TAG_EDDSA_PRIVATEKEY; + priv.elements[0].length = len; + ret = priv_ed25519_from_ossl(pkey, buf); + if (ret != ISC_R_SUCCESS) + DST_RET (dst__openssl_toresult(ret)); + priv.elements[0].data = buf; + priv.nelements = 1; + ret = dst__privstruct_writefile(key, &priv, directory); + } else { + len = DNS_KEY_ED448SIZE; + buf = isc_mem_get(key->mctx, len); + if (buf == NULL) + return (ISC_R_NOMEMORY); + priv.elements[0].tag = TAG_EDDSA_PRIVATEKEY; + priv.elements[0].length = len; + ret = priv_ed448_from_ossl(pkey, buf); + if (ret != ISC_R_SUCCESS) + DST_RET (dst__openssl_toresult(ret)); + priv.elements[0].data = buf; + priv.nelements = 1; + ret = dst__privstruct_writefile(key, &priv, directory); + } + + err: + if (buf != NULL) + isc_mem_put(key->mctx, buf, len); + return (ret); +} + +static isc_result_t +eddsa_check(EVP_PKEY *privkey, dst_key_t *pub) +{ + EVP_PKEY *pkey; + + if (pub == NULL) + return (ISC_R_SUCCESS); + pkey = pub->keydata.pkey; + if (pkey == NULL) + return (ISC_R_SUCCESS); + if (EVP_PKEY_cmp(privkey, pkey) == 1) + return (ISC_R_SUCCESS); + return (ISC_R_FAILURE); +} + +static isc_result_t +openssleddsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { + dst_private_t priv; + isc_result_t ret; + EVP_PKEY *pkey = NULL; + unsigned int len; + isc_mem_t *mctx = key->mctx; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + + /* read private key file */ + ret = dst__privstruct_parse(key, DST_ALG_ED25519, lexer, mctx, &priv); + if (ret != ISC_R_SUCCESS) + goto err; + + if (key->external) { + if (priv.nelements != 0) + DST_RET(DST_R_INVALIDPRIVATEKEY); + if (pub == NULL) + DST_RET(DST_R_INVALIDPRIVATEKEY); + key->keydata.pkey = pub->keydata.pkey; + pub->keydata.pkey = NULL; + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ISC_R_SUCCESS); + } + + if (key->key_alg == DST_ALG_ED25519) { + len = DNS_KEY_ED25519SIZE; + if (priv.elements[0].length < len) + DST_RET(DST_R_INVALIDPRIVATEKEY); + pkey = priv_ed25519_to_ossl(priv.elements[0].data); + } else { + len = DNS_KEY_ED448SIZE; + if (priv.elements[0].length < len) + DST_RET(DST_R_INVALIDPRIVATEKEY); + pkey = priv_ed448_to_ossl(priv.elements[0].data); + } + if (pkey == NULL) + DST_RET (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + if (eddsa_check(pkey, pub) != ISC_R_SUCCESS) { + EVP_PKEY_free(pkey); + DST_RET(DST_R_INVALIDPRIVATEKEY); + } + key->keydata.pkey = pkey; + key->key_size = len; + ret = ISC_R_SUCCESS; + + err: + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ret); +} + +static dst_func_t openssleddsa_functions = { + openssleddsa_createctx, + openssleddsa_destroyctx, + openssleddsa_adddata, + openssleddsa_sign, + openssleddsa_verify, + NULL, /*%< verify2 */ + NULL, /*%< computesecret */ + openssleddsa_compare, + NULL, /*%< paramcompare */ + openssleddsa_generate, + openssleddsa_isprivate, + openssleddsa_destroy, + openssleddsa_todns, + openssleddsa_fromdns, + openssleddsa_tofile, + openssleddsa_parse, + NULL, /*%< cleanup */ + NULL, /*%< fromlabel */ + NULL, /*%< dump */ + NULL, /*%< restore */ +}; + +isc_result_t +dst__openssleddsa_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) + *funcp = &openssleddsa_functions; + return (ISC_R_SUCCESS); +} + +#else /* HAVE_OPENSSL_EDxxx */ + +#include + +EMPTY_TRANSLATION_UNIT + +#endif /* HAVE_OPENSSL_EDxxx */ +/*! \file */ diff --git a/lib/dns/pkcs11eddsa_link.c b/lib/dns/pkcs11eddsa_link.c new file mode 100644 index 0000000000..626ef4c940 --- /dev/null +++ b/lib/dns/pkcs11eddsa_link.c @@ -0,0 +1,1179 @@ +/* + * Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#if defined(PKCS11CRYPTO) && \ + defined(HAVE_PKCS11_ED25519) || defined(HAVE_PKCS11_ED448) + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dst_internal.h" +#include "dst_parse.h" +#include "dst_pkcs11.h" + +#include +#include +#define WANT_ECC_CURVES +#include + +#include +#include + +/* + * FIPS 186-3 EDDSA keys: + * mechanisms: + * CKM_EDDSA, + * CKM_EDDSA_KEY_PAIR_GEN + * domain parameters: + * CKA_EC_PARAMS (choice with OID namedCurve) + * public keys: + * object class CKO_PUBLIC_KEY + * key type CKK_EDDSA + * attribute CKA_EC_PARAMS (choice with OID namedCurve) + * attribute CKA_EC_POINT (big int A, CKA_VALUE on the token) + * private keys: + * object class CKO_PRIVATE_KEY + * key type CKK_EDDSA + * attribute CKA_EC_PARAMS (choice with OID namedCurve) + * attribute CKA_VALUE (big int k) + */ + +#define DST_RET(a) {ret = a; goto err;} + +static CK_BBOOL truevalue = TRUE; +static CK_BBOOL falsevalue = FALSE; + +static isc_result_t pkcs11eddsa_todns(const dst_key_t *key, + isc_buffer_t *data); +static void pkcs11eddsa_destroy(dst_key_t *key); +static isc_result_t pkcs11eddsa_fetch(dst_key_t *key, const char *engine, + const char *label, dst_key_t *pub); + +static isc_result_t +pkcs11eddsa_createctx(dst_key_t *key, dst_context_t *dctx) { + isc_buffer_t *buf = NULL; + isc_result_t result; + + UNUSED(key); + REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || + dctx->key->key_alg == DST_ALG_ED448); + + result = isc_buffer_allocate(dctx->mctx, &buf, 16); + isc_buffer_setautorealloc(buf, ISC_TRUE); + dctx->ctxdata.generic = buf; + + return (result); +} + +static void +pkcs11eddsa_destroyctx(dst_context_t *dctx) { + isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic; + + REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || + dctx->key->key_alg == DST_ALG_ED448); + if (buf != NULL) + isc_buffer_free(&buf); + dctx->ctxdata.generic = NULL; +} + +static isc_result_t +pkcs11eddsa_adddata(dst_context_t *dctx, const isc_region_t *data) { + isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic; + isc_buffer_t *nbuf = NULL; + isc_region_t r; + unsigned int length; + isc_result_t result; + + REQUIRE(dctx->key->key_alg == DST_ALG_ED25519 || + dctx->key->key_alg == DST_ALG_ED448); + + result = isc_buffer_copyregion(buf, data); + if (result == ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + + length = isc_buffer_length(buf) + data->length + 64; + result = isc_buffer_allocate(dctx->mctx, &nbuf, length); + if (result != ISC_R_SUCCESS) + return (result); + isc_buffer_usedregion(buf, &r); + (void) isc_buffer_copyregion(nbuf, &r); + (void) isc_buffer_copyregion(nbuf, data); + isc_buffer_free(&buf); + dctx->ctxdata.generic = nbuf; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +pkcs11eddsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { + isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic; + CK_RV rv; + CK_MECHANISM mech = { CKM_EDDSA, NULL, 0 }; + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE keyType = CKK_EDDSA; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_EC_PARAMS, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ATTRIBUTE *attr; + CK_ULONG siglen; + CK_SLOT_ID slotid; + pk11_context_t *pk11_ctx; + dst_key_t *key = dctx->key; + pk11_object_t *ec = key->keydata.pkey; + isc_region_t t; + isc_region_t r; + isc_result_t ret = ISC_R_SUCCESS; + unsigned int i; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + REQUIRE(ec != NULL); + + if (key->key_alg == DST_ALG_ED25519) + siglen = DNS_SIG_ED25519SIZE; + else + siglen = DNS_SIG_ED448SIZE; + + pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx, + sizeof(*pk11_ctx)); + if (pk11_ctx == NULL) + return (ISC_R_NOMEMORY); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + if (ec->ontoken && (dctx->use == DO_SIGN)) + slotid = ec->slot; + else + slotid = pk11_get_best_token(OP_EC); + ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE, + ec->reqlogon, NULL, slotid); + if (ret != ISC_R_SUCCESS) + goto err; + + isc_buffer_availableregion(sig, &r); + if (r.length < siglen) + DST_RET(ISC_R_NOSPACE); + + if (ec->ontoken && (ec->object != CK_INVALID_HANDLE)) { + pk11_ctx->ontoken = ec->ontoken; + pk11_ctx->object = ec->object; + goto token_key; + } + + for (attr = pk11_attribute_first(ec); + attr != NULL; + attr = pk11_attribute_next(ec, attr)) + switch (attr->type) { + case CKA_EC_PARAMS: + INSIST(keyTemplate[5].type == attr->type); + keyTemplate[5].pValue = isc_mem_get(dctx->mctx, + attr->ulValueLen); + if (keyTemplate[5].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[5].pValue, attr->pValue, + attr->ulValueLen); + keyTemplate[5].ulValueLen = attr->ulValueLen; + break; + case CKA_VALUE: + INSIST(keyTemplate[6].type == attr->type); + keyTemplate[6].pValue = isc_mem_get(dctx->mctx, + attr->ulValueLen); + if (keyTemplate[6].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[6].pValue, attr->pValue, + attr->ulValueLen); + keyTemplate[6].ulValueLen = attr->ulValueLen; + break; + } + pk11_ctx->object = CK_INVALID_HANDLE; + pk11_ctx->ontoken = ISC_FALSE; + PK11_RET(pkcs_C_CreateObject, + (pk11_ctx->session, + keyTemplate, (CK_ULONG) 7, + &hKey), + ISC_R_FAILURE); + + token_key: + + PK11_RET(pkcs_C_SignInit, + (pk11_ctx->session, &mech, + pk11_ctx->ontoken ? pk11_ctx->object : hKey), + ISC_R_FAILURE); + + isc_buffer_usedregion(buf, &t); + + PK11_RET(pkcs_C_Sign, + (pk11_ctx->session, + (CK_BYTE_PTR) t.base, (CK_ULONG) t.length, + (CK_BYTE_PTR) r.base, &siglen), + DST_R_SIGNFAILURE); + + isc_buffer_add(sig, (unsigned int) siglen); + + err: + + if (hKey != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(pk11_ctx->session, hKey); + for (i = 5; i <= 6; i++) + if (keyTemplate[i].pValue != NULL) { + memset(keyTemplate[i].pValue, 0, + keyTemplate[i].ulValueLen); + isc_mem_put(dctx->mctx, + keyTemplate[i].pValue, + keyTemplate[i].ulValueLen); + } + pk11_return_session(pk11_ctx); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); + isc_buffer_free(&buf); + dctx->ctxdata.generic = NULL; + + return (ret); +} + +static isc_result_t +pkcs11eddsa_verify(dst_context_t *dctx, const isc_region_t *sig) { + isc_buffer_t *buf = (isc_buffer_t *) dctx->ctxdata.generic; + CK_RV rv; + CK_MECHANISM mech = { CKM_EDDSA, NULL, 0 }; + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE keyType = CKK_EDDSA; + CK_ATTRIBUTE keyTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_EC_PARAMS, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + CK_ATTRIBUTE *attr; + CK_SLOT_ID slotid; + pk11_context_t *pk11_ctx; + dst_key_t *key = dctx->key; + pk11_object_t *ec = key->keydata.pkey; + isc_region_t t; + isc_result_t ret = ISC_R_SUCCESS; + unsigned int i; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + REQUIRE(ec != NULL); + + pk11_ctx = (pk11_context_t *) isc_mem_get(dctx->mctx, + sizeof(*pk11_ctx)); + if (pk11_ctx == NULL) + return (ISC_R_NOMEMORY); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + if (ec->ontoken && (dctx->use == DO_SIGN)) + slotid = ec->slot; + else + slotid = pk11_get_best_token(OP_EC); + ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE, + ec->reqlogon, NULL, slotid); + if (ret != ISC_R_SUCCESS) + goto err; + + for (attr = pk11_attribute_first(ec); + attr != NULL; + attr = pk11_attribute_next(ec, attr)) + switch (attr->type) { + case CKA_EC_PARAMS: + INSIST(keyTemplate[5].type == attr->type); + keyTemplate[5].pValue = isc_mem_get(dctx->mctx, + attr->ulValueLen); + if (keyTemplate[5].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[5].pValue, attr->pValue, + attr->ulValueLen); + keyTemplate[5].ulValueLen = attr->ulValueLen; + break; + case CKA_EC_POINT: + /* keyTemplate[6].type is CKA_VALUE */ + keyTemplate[6].pValue = isc_mem_get(dctx->mctx, + attr->ulValueLen); + if (keyTemplate[6].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(keyTemplate[6].pValue, attr->pValue, + attr->ulValueLen); + keyTemplate[6].ulValueLen = attr->ulValueLen; + break; + } + pk11_ctx->object = CK_INVALID_HANDLE; + pk11_ctx->ontoken = ISC_FALSE; + PK11_RET(pkcs_C_CreateObject, + (pk11_ctx->session, + keyTemplate, (CK_ULONG) 7, + &hKey), + ISC_R_FAILURE); + + PK11_RET(pkcs_C_VerifyInit, + (pk11_ctx->session, &mech, hKey), + ISC_R_FAILURE); + + isc_buffer_usedregion(buf, &t); + + PK11_RET(pkcs_C_Verify, + (pk11_ctx->session, + (CK_BYTE_PTR) t.base, (CK_ULONG) t.length, + (CK_BYTE_PTR) sig->base, (CK_ULONG) sig->length), + DST_R_VERIFYFAILURE); + + err: + + if (hKey != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(pk11_ctx->session, hKey); + for (i = 5; i <= 6; i++) + if (keyTemplate[i].pValue != NULL) { + memset(keyTemplate[i].pValue, 0, + keyTemplate[i].ulValueLen); + isc_mem_put(dctx->mctx, + keyTemplate[i].pValue, + keyTemplate[i].ulValueLen); + } + pk11_return_session(pk11_ctx); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + isc_mem_put(dctx->mctx, pk11_ctx, sizeof(*pk11_ctx)); + isc_buffer_free(&buf); + dctx->ctxdata.generic = NULL; + + return (ret); +} + +static isc_boolean_t +pkcs11eddsa_compare(const dst_key_t *key1, const dst_key_t *key2) { + pk11_object_t *ec1, *ec2; + CK_ATTRIBUTE *attr1, *attr2; + + ec1 = key1->keydata.pkey; + ec2 = key2->keydata.pkey; + + if ((ec1 == NULL) && (ec2 == NULL)) + return (ISC_TRUE); + else if ((ec1 == NULL) || (ec2 == NULL)) + return (ISC_FALSE); + + attr1 = pk11_attribute_bytype(ec1, CKA_EC_PARAMS); + attr2 = pk11_attribute_bytype(ec2, CKA_EC_PARAMS); + if ((attr1 == NULL) && (attr2 == NULL)) + return (ISC_TRUE); + else if ((attr1 == NULL) || (attr2 == NULL) || + (attr1->ulValueLen != attr2->ulValueLen) || + !isc_safe_memequal(attr1->pValue, attr2->pValue, + attr1->ulValueLen)) + return (ISC_FALSE); + + attr1 = pk11_attribute_bytype(ec1, CKA_EC_POINT); + attr2 = pk11_attribute_bytype(ec2, CKA_EC_POINT); + if ((attr1 == NULL) && (attr2 == NULL)) + return (ISC_TRUE); + else if ((attr1 == NULL) || (attr2 == NULL) || + (attr1->ulValueLen != attr2->ulValueLen) || + !isc_safe_memequal(attr1->pValue, attr2->pValue, + attr1->ulValueLen)) + return (ISC_FALSE); + + attr1 = pk11_attribute_bytype(ec1, CKA_VALUE); + attr2 = pk11_attribute_bytype(ec2, CKA_VALUE); + if (((attr1 != NULL) || (attr2 != NULL)) && + ((attr1 == NULL) || (attr2 == NULL) || + (attr1->ulValueLen != attr2->ulValueLen) || + !isc_safe_memequal(attr1->pValue, attr2->pValue, + attr1->ulValueLen))) + return (ISC_FALSE); + + if (!ec1->ontoken && !ec2->ontoken) + return (ISC_TRUE); + else if (ec1->ontoken || ec2->ontoken || + (ec1->object != ec2->object)) + return (ISC_FALSE); + + return (ISC_TRUE); +} + +#define SETCURVE() \ + if (key->key_alg == DST_ALG_ED25519) { \ + attr->pValue = isc_mem_get(key->mctx, \ + sizeof(pk11_ecc_ed25519)); \ + if (attr->pValue == NULL) \ + DST_RET(ISC_R_NOMEMORY); \ + memmove(attr->pValue, \ + pk11_ecc_ed25519, sizeof(pk11_ecc_ed25519)); \ + attr->ulValueLen = sizeof(pk11_ecc_ed25519); \ + } else { \ + attr->pValue = isc_mem_get(key->mctx, \ + sizeof(pk11_ecc_ed448)); \ + if (attr->pValue == NULL) \ + DST_RET(ISC_R_NOMEMORY); \ + memmove(attr->pValue, \ + pk11_ecc_ed448, sizeof(pk11_ecc_ed448)); \ + attr->ulValueLen = sizeof(pk11_ecc_ed448); \ + } + +#define FREECURVE() \ + if (attr->pValue != NULL) { \ + memset(attr->pValue, 0, attr->ulValueLen); \ + isc_mem_put(key->mctx, attr->pValue, attr->ulValueLen); \ + attr->pValue = NULL; \ + } + +static isc_result_t +pkcs11eddsa_generate(dst_key_t *key, int unused, void (*callback)(int)) { + CK_RV rv; + CK_MECHANISM mech = { CKM_EDDSA_KEY_PAIR_GEN, NULL, 0 }; + CK_OBJECT_HANDLE pub = CK_INVALID_HANDLE; + CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE keyType = CKK_EDDSA; + CK_ATTRIBUTE pubTemplate[] = + { + { CKA_CLASS, &pubClass, (CK_ULONG) sizeof(pubClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_VERIFY, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_EC_PARAMS, NULL, 0 } + }; + CK_OBJECT_HANDLE priv = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE privClass = CKO_PRIVATE_KEY; + CK_ATTRIBUTE privTemplate[] = + { + { CKA_CLASS, &privClass, (CK_ULONG) sizeof(privClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_PRIVATE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_SENSITIVE, &falsevalue, (CK_ULONG) sizeof(falsevalue) }, + { CKA_EXTRACTABLE, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_SIGN, &truevalue, (CK_ULONG) sizeof(truevalue) } + }; + CK_ATTRIBUTE *attr; + pk11_object_t *ec; + pk11_context_t *pk11_ctx; + isc_result_t ret; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + UNUSED(unused); + UNUSED(callback); + + pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx, + sizeof(*pk11_ctx)); + if (pk11_ctx == NULL) + return (ISC_R_NOMEMORY); + ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE, + ISC_FALSE, NULL, pk11_get_best_token(OP_EC)); + if (ret != ISC_R_SUCCESS) + goto err; + + ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec)); + if (ec == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(ec, 0, sizeof(*ec)); + key->keydata.pkey = ec; + ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3); + if (ec->repr == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(ec->repr, 0, sizeof(*attr) * 3); + ec->attrcnt = 3; + + attr = ec->repr; + attr[0].type = CKA_EC_PARAMS; + attr[1].type = CKA_VALUE; + attr[2].type = CKA_VALUE; + + attr = &pubTemplate[5]; + SETCURVE(); + + PK11_RET(pkcs_C_GenerateKeyPair, + (pk11_ctx->session, &mech, + pubTemplate, (CK_ULONG) 6, + privTemplate, (CK_ULONG) 7, + &pub, &priv), + DST_R_CRYPTOFAILURE); + + attr = &pubTemplate[5]; + FREECURVE(); + + attr = ec->repr; + SETCURVE(); + + attr++; + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, pub, attr, 1), + DST_R_CRYPTOFAILURE); + attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(attr->pValue, 0, attr->ulValueLen); + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, pub, attr, 1), + DST_R_CRYPTOFAILURE); + attr->type = CKA_EC_POINT; + + attr++; + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, priv, attr, 1), + DST_R_CRYPTOFAILURE); + attr->pValue = isc_mem_get(key->mctx, attr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(attr->pValue, 0, attr->ulValueLen); + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, priv, attr, 1), + DST_R_CRYPTOFAILURE); + + (void) pkcs_C_DestroyObject(pk11_ctx->session, priv); + (void) pkcs_C_DestroyObject(pk11_ctx->session, pub); + pk11_return_session(pk11_ctx); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); + + if (key->key_alg == DST_ALG_ED25519) + key->key_size = DNS_KEY_ED25519SIZE; + else + key->key_size = DNS_KEY_ED448SIZE; + + return (ISC_R_SUCCESS); + + err: + pkcs11eddsa_destroy(key); + if (priv != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(pk11_ctx->session, priv); + if (pub != CK_INVALID_HANDLE) + (void) pkcs_C_DestroyObject(pk11_ctx->session, pub); + pk11_return_session(pk11_ctx); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); + + return (ret); +} + +static isc_boolean_t +pkcs11eddsa_isprivate(const dst_key_t *key) { + pk11_object_t *ec = key->keydata.pkey; + CK_ATTRIBUTE *attr; + + if (ec == NULL) + return (ISC_FALSE); + attr = pk11_attribute_bytype(ec, CKA_VALUE); + return (ISC_TF((attr != NULL) || ec->ontoken)); +} + +static void +pkcs11eddsa_destroy(dst_key_t *key) { + pk11_object_t *ec = key->keydata.pkey; + CK_ATTRIBUTE *attr; + + if (ec == NULL) + return; + + INSIST((ec->object == CK_INVALID_HANDLE) || ec->ontoken); + + for (attr = pk11_attribute_first(ec); + attr != NULL; + attr = pk11_attribute_next(ec, attr)) + switch (attr->type) { + case CKA_LABEL: + case CKA_ID: + case CKA_EC_PARAMS: + case CKA_EC_POINT: + case CKA_VALUE: + FREECURVE(); + break; + } + if (ec->repr != NULL) { + memset(ec->repr, 0, ec->attrcnt * sizeof(*attr)); + isc_mem_put(key->mctx, + ec->repr, + ec->attrcnt * sizeof(*attr)); + } + memset(ec, 0, sizeof(*ec)); + isc_mem_put(key->mctx, ec, sizeof(*ec)); + key->keydata.pkey = NULL; +} + +static isc_result_t +pkcs11eddsa_todns(const dst_key_t *key, isc_buffer_t *data) { + pk11_object_t *ec; + isc_region_t r; + unsigned int len; + CK_ATTRIBUTE *attr; + + REQUIRE(key->keydata.pkey != NULL); + + if (key->key_alg == DST_ALG_ED25519) + len = DNS_KEY_ED25519SIZE; + else + len = DNS_KEY_ED448SIZE; + + ec = key->keydata.pkey; + attr = pk11_attribute_bytype(ec, CKA_EC_POINT); + if ((attr == NULL) || (attr->ulValueLen != len)) + return (ISC_R_FAILURE); + + isc_buffer_availableregion(data, &r); + if (r.length < len) + return (ISC_R_NOSPACE); + memmove(r.base, (CK_BYTE_PTR) attr->pValue, len); + isc_buffer_add(data, len); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +pkcs11eddsa_fromdns(dst_key_t *key, isc_buffer_t *data) { + pk11_object_t *ec; + isc_region_t r; + unsigned int len; + CK_ATTRIBUTE *attr; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + + if (key->key_alg == DST_ALG_ED25519) + len = DNS_KEY_ED25519SIZE; + else + len = DNS_KEY_ED448SIZE; + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) + return (ISC_R_SUCCESS); + if (r.length != len) + return (DST_R_INVALIDPUBLICKEY); + + ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec)); + if (ec == NULL) + return (ISC_R_NOMEMORY); + memset(ec, 0, sizeof(*ec)); + ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2); + if (ec->repr == NULL) + goto nomemory; + ec->attrcnt = 2; + + attr = ec->repr; + attr->type = CKA_EC_PARAMS; + if (key->key_alg == DST_ALG_ED25519) { + attr->pValue = + isc_mem_get(key->mctx, sizeof(pk11_ecc_ed25519)); + if (attr->pValue == NULL) + goto nomemory; + memmove(attr->pValue, + pk11_ecc_ed25519, sizeof(pk11_ecc_ed25519)); + attr->ulValueLen = sizeof(pk11_ecc_ed25519); + } else { + attr->pValue = + isc_mem_get(key->mctx, sizeof(pk11_ecc_ed448)); + if (attr->pValue == NULL) + goto nomemory; + memmove(attr->pValue, + pk11_ecc_ed448, sizeof(pk11_ecc_ed448)); + attr->ulValueLen = sizeof(pk11_ecc_ed448); + } + + attr++; + attr->type = CKA_EC_POINT; + attr->pValue = isc_mem_get(key->mctx, len); + if (attr->pValue == NULL) + goto nomemory; + memmove((CK_BYTE_PTR) attr->pValue, r.base, len); + attr->ulValueLen = len; + + isc_buffer_forward(data, len); + key->keydata.pkey = ec; + key->key_size = len; + return (ISC_R_SUCCESS); + + nomemory: + for (attr = pk11_attribute_first(ec); + attr != NULL; + attr = pk11_attribute_next(ec, attr)) + switch (attr->type) { + case CKA_EC_PARAMS: + case CKA_EC_POINT: + FREECURVE(); + break; + } + if (ec->repr != NULL) { + memset(ec->repr, 0, ec->attrcnt * sizeof(*attr)); + isc_mem_put(key->mctx, + ec->repr, + ec->attrcnt * sizeof(*attr)); + } + memset(ec, 0, sizeof(*ec)); + isc_mem_put(key->mctx, ec, sizeof(*ec)); + return (ISC_R_NOMEMORY); +} + +static isc_result_t +pkcs11eddsa_tofile(const dst_key_t *key, const char *directory) { + isc_result_t ret; + pk11_object_t *ec; + dst_private_t priv; + unsigned char *buf = NULL; + unsigned int i = 0; + CK_ATTRIBUTE *attr; + + if (key->keydata.pkey == NULL) + return (DST_R_NULLKEY); + + if (key->external) { + priv.nelements = 0; + return (dst__privstruct_writefile(key, &priv, directory)); + } + + ec = key->keydata.pkey; + attr = pk11_attribute_bytype(ec, CKA_VALUE); + if (attr != NULL) { + buf = isc_mem_get(key->mctx, attr->ulValueLen); + if (buf == NULL) + return (ISC_R_NOMEMORY); + priv.elements[i].tag = TAG_EDDSA_PRIVATEKEY; + priv.elements[i].length = (unsigned short) attr->ulValueLen; + memmove(buf, attr->pValue, attr->ulValueLen); + priv.elements[i].data = buf; + i++; + } + + if (key->engine != NULL) { + priv.elements[i].tag = TAG_EDDSA_ENGINE; + priv.elements[i].length = strlen(key->engine) + 1; + priv.elements[i].data = (unsigned char *)key->engine; + i++; + } + + if (key->label != NULL) { + priv.elements[i].tag = TAG_EDDSA_LABEL; + priv.elements[i].length = strlen(key->label) + 1; + priv.elements[i].data = (unsigned char *)key->label; + i++; + } + + priv.nelements = i; + ret = dst__privstruct_writefile(key, &priv, directory); + + if (buf != NULL) { + memset(buf, 0, attr->ulValueLen); + isc_mem_put(key->mctx, buf, attr->ulValueLen); + } + return (ret); +} + +static isc_result_t +pkcs11eddsa_fetch(dst_key_t *key, const char *engine, const char *label, + dst_key_t *pub) +{ + CK_RV rv; + CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY; + CK_KEY_TYPE keyType = CKK_EDDSA; + CK_ATTRIBUTE searchTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_LABEL, NULL, 0 } + }; + CK_ULONG cnt; + CK_ATTRIBUTE *attr; + CK_ATTRIBUTE *pubattr; + pk11_object_t *ec; + pk11_object_t *pubec; + pk11_context_t *pk11_ctx = NULL; + isc_result_t ret; + + if (label == NULL) + return (DST_R_NOENGINE); + + ec = key->keydata.pkey; + pubec = pub->keydata.pkey; + + ec->object = CK_INVALID_HANDLE; + ec->ontoken = ISC_TRUE; + ec->reqlogon = ISC_TRUE; + ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2); + if (ec->repr == NULL) + return (ISC_R_NOMEMORY); + memset(ec->repr, 0, sizeof(*attr) * 2); + ec->attrcnt = 2; + attr = ec->repr; + + attr->type = CKA_EC_PARAMS; + pubattr = pk11_attribute_bytype(pubec, CKA_EC_PARAMS); + attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen); + attr->ulValueLen = pubattr->ulValueLen; + attr++; + + attr->type = CKA_EC_POINT; + pubattr = pk11_attribute_bytype(pubec, CKA_EC_POINT); + attr->pValue = isc_mem_get(key->mctx, pubattr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(attr->pValue, pubattr->pValue, pubattr->ulValueLen); + attr->ulValueLen = pubattr->ulValueLen; + + ret = pk11_parse_uri(ec, label, key->mctx, OP_EC); + if (ret != ISC_R_SUCCESS) + goto err; + + pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx, + sizeof(*pk11_ctx)); + if (pk11_ctx == NULL) + DST_RET(ISC_R_NOMEMORY); + ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE, + ec->reqlogon, NULL, ec->slot); + if (ret != ISC_R_SUCCESS) + goto err; + + attr = pk11_attribute_bytype(ec, CKA_LABEL); + if (attr == NULL) { + attr = pk11_attribute_bytype(ec, CKA_ID); + INSIST(attr != NULL); + searchTemplate[3].type = CKA_ID; + } + searchTemplate[3].pValue = attr->pValue; + searchTemplate[3].ulValueLen = attr->ulValueLen; + + PK11_RET(pkcs_C_FindObjectsInit, + (pk11_ctx->session, searchTemplate, (CK_ULONG) 4), + DST_R_CRYPTOFAILURE); + PK11_RET(pkcs_C_FindObjects, + (pk11_ctx->session, &ec->object, (CK_ULONG) 1, &cnt), + DST_R_CRYPTOFAILURE); + (void) pkcs_C_FindObjectsFinal(pk11_ctx->session); + if (cnt == 0) + DST_RET(ISC_R_NOTFOUND); + if (cnt > 1) + DST_RET(ISC_R_EXISTS); + + if (engine != NULL) { + key->engine = isc_mem_strdup(key->mctx, engine); + if (key->engine == NULL) + DST_RET(ISC_R_NOMEMORY); + } + + key->label = isc_mem_strdup(key->mctx, label); + if (key->label == NULL) + DST_RET(ISC_R_NOMEMORY); + + pk11_return_session(pk11_ctx); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); + return (ISC_R_SUCCESS); + + err: + if (pk11_ctx != NULL) { + pk11_return_session(pk11_ctx); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); + } + return (ret); +} + +static isc_result_t +pkcs11eddsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) { + dst_private_t priv; + isc_result_t ret; + pk11_object_t *ec = NULL; + CK_ATTRIBUTE *attr, *pattr; + isc_mem_t *mctx = key->mctx; + unsigned int i; + const char *engine = NULL, *label = NULL; + + REQUIRE(key->key_alg == DST_ALG_ED25519 || + key->key_alg == DST_ALG_ED448); + + if ((pub == NULL) || (pub->keydata.pkey == NULL)) + DST_RET(DST_R_INVALIDPRIVATEKEY); + + /* read private key file */ + ret = dst__privstruct_parse(key, DST_ALG_ED25519, lexer, mctx, &priv); + if (ret != ISC_R_SUCCESS) + return (ret); + + if (key->external) { + if (priv.nelements != 0) + DST_RET(DST_R_INVALIDPRIVATEKEY); + + key->keydata.pkey = pub->keydata.pkey; + pub->keydata.pkey = NULL; + key->key_size = pub->key_size; + + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + + return (ISC_R_SUCCESS); + } + + for (i = 0; i < priv.nelements; i++) { + switch (priv.elements[i].tag) { + case TAG_EDDSA_ENGINE: + engine = (char *)priv.elements[i].data; + break; + case TAG_EDDSA_LABEL: + label = (char *)priv.elements[i].data; + break; + default: + break; + } + } + ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec)); + if (ec == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(ec, 0, sizeof(*ec)); + key->keydata.pkey = ec; + + /* Is this key is stored in a HSM? See if we can fetch it. */ + if ((label != NULL) || (engine != NULL)) { + ret = pkcs11eddsa_fetch(key, engine, label, pub); + if (ret != ISC_R_SUCCESS) + goto err; + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ret); + } + + ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 3); + if (ec->repr == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(ec->repr, 0, sizeof(*attr) * 3); + ec->attrcnt = 3; + + attr = ec->repr; + attr->type = CKA_EC_PARAMS; + pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_PARAMS); + INSIST(pattr != NULL); + attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(attr->pValue, pattr->pValue, pattr->ulValueLen); + attr->ulValueLen = pattr->ulValueLen; + + attr++; + attr->type = CKA_EC_POINT; + pattr = pk11_attribute_bytype(pub->keydata.pkey, CKA_EC_POINT); + INSIST(pattr != NULL); + attr->pValue = isc_mem_get(key->mctx, pattr->ulValueLen); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(attr->pValue, pattr->pValue, pattr->ulValueLen); + attr->ulValueLen = pattr->ulValueLen; + + attr++; + attr->type = CKA_VALUE; + attr->pValue = isc_mem_get(key->mctx, priv.elements[0].length); + if (attr->pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memmove(attr->pValue, priv.elements[0].data, priv.elements[0].length); + attr->ulValueLen = priv.elements[0].length; + + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + if (key->key_alg == DST_ALG_ED25519) + key->key_size = DNS_KEY_ED25519SIZE; + else + key->key_size = DNS_KEY_ED448SIZE; + + return (ISC_R_SUCCESS); + + err: + pkcs11eddsa_destroy(key); + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ret); +} + +static isc_result_t +pkcs11eddsa_fromlabel(dst_key_t *key, const char *engine, const char *label, + const char *pin) +{ + CK_RV rv; + CK_OBJECT_HANDLE hKey = CK_INVALID_HANDLE; + CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY; + CK_KEY_TYPE keyType = CKK_EDDSA; + CK_ATTRIBUTE searchTemplate[] = + { + { CKA_CLASS, &keyClass, (CK_ULONG) sizeof(keyClass) }, + { CKA_KEY_TYPE, &keyType, (CK_ULONG) sizeof(keyType) }, + { CKA_TOKEN, &truevalue, (CK_ULONG) sizeof(truevalue) }, + { CKA_LABEL, NULL, 0 } + }; + CK_ULONG cnt; + CK_ATTRIBUTE *attr; + pk11_object_t *ec; + pk11_context_t *pk11_ctx = NULL; + isc_result_t ret; + unsigned int i; + + UNUSED(pin); + + ec = (pk11_object_t *) isc_mem_get(key->mctx, sizeof(*ec)); + if (ec == NULL) + return (ISC_R_NOMEMORY); + memset(ec, 0, sizeof(*ec)); + ec->object = CK_INVALID_HANDLE; + ec->ontoken = ISC_TRUE; + ec->reqlogon = ISC_TRUE; + key->keydata.pkey = ec; + + ec->repr = (CK_ATTRIBUTE *) isc_mem_get(key->mctx, sizeof(*attr) * 2); + if (ec->repr == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(ec->repr, 0, sizeof(*attr) * 2); + ec->attrcnt = 2; + attr = ec->repr; + attr[0].type = CKA_EC_PARAMS; + attr[1].type = CKA_VALUE; + + ret = pk11_parse_uri(ec, label, key->mctx, OP_EC); + if (ret != ISC_R_SUCCESS) + goto err; + + pk11_ctx = (pk11_context_t *) isc_mem_get(key->mctx, + sizeof(*pk11_ctx)); + if (pk11_ctx == NULL) + DST_RET(ISC_R_NOMEMORY); + ret = pk11_get_session(pk11_ctx, OP_EC, ISC_TRUE, ISC_FALSE, + ec->reqlogon, NULL, ec->slot); + if (ret != ISC_R_SUCCESS) + goto err; + + attr = pk11_attribute_bytype(ec, CKA_LABEL); + if (attr == NULL) { + attr = pk11_attribute_bytype(ec, CKA_ID); + INSIST(attr != NULL); + searchTemplate[3].type = CKA_ID; + } + searchTemplate[3].pValue = attr->pValue; + searchTemplate[3].ulValueLen = attr->ulValueLen; + + PK11_RET(pkcs_C_FindObjectsInit, + (pk11_ctx->session, searchTemplate, (CK_ULONG) 4), + DST_R_CRYPTOFAILURE); + PK11_RET(pkcs_C_FindObjects, + (pk11_ctx->session, &hKey, (CK_ULONG) 1, &cnt), + DST_R_CRYPTOFAILURE); + (void) pkcs_C_FindObjectsFinal(pk11_ctx->session); + if (cnt == 0) + DST_RET(ISC_R_NOTFOUND); + if (cnt > 1) + DST_RET(ISC_R_EXISTS); + + attr = ec->repr; + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, hKey, attr, 2), + DST_R_CRYPTOFAILURE); + for (i = 0; i <= 1; i++) { + attr[i].pValue = isc_mem_get(key->mctx, attr[i].ulValueLen); + if (attr[i].pValue == NULL) + DST_RET(ISC_R_NOMEMORY); + memset(attr[i].pValue, 0, attr[i].ulValueLen); + } + PK11_RET(pkcs_C_GetAttributeValue, + (pk11_ctx->session, hKey, attr, 2), + DST_R_CRYPTOFAILURE); + attr[1].type = CKA_EC_POINT; + + keyClass = CKO_PRIVATE_KEY; + PK11_RET(pkcs_C_FindObjectsInit, + (pk11_ctx->session, searchTemplate, (CK_ULONG) 4), + DST_R_CRYPTOFAILURE); + PK11_RET(pkcs_C_FindObjects, + (pk11_ctx->session, &ec->object, (CK_ULONG) 1, &cnt), + DST_R_CRYPTOFAILURE); + (void) pkcs_C_FindObjectsFinal(pk11_ctx->session); + if (cnt == 0) + DST_RET(ISC_R_NOTFOUND); + if (cnt > 1) + DST_RET(ISC_R_EXISTS); + + if (engine != NULL) { + key->engine = isc_mem_strdup(key->mctx, engine); + if (key->engine == NULL) + DST_RET(ISC_R_NOMEMORY); + } + + key->label = isc_mem_strdup(key->mctx, label); + if (key->label == NULL) + DST_RET(ISC_R_NOMEMORY); + if (key->key_alg == DST_ALG_ED25519) + key->key_size = DNS_KEY_ED25519SIZE; + else + key->key_size = DNS_KEY_ED448SIZE; + + pk11_return_session(pk11_ctx); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); + return (ISC_R_SUCCESS); + + err: + pkcs11eddsa_destroy(key); + if (pk11_ctx != NULL) { + pk11_return_session(pk11_ctx); + memset(pk11_ctx, 0, sizeof(*pk11_ctx)); + isc_mem_put(key->mctx, pk11_ctx, sizeof(*pk11_ctx)); + } + return (ret); +} + +static dst_func_t pkcs11eddsa_functions = { + pkcs11eddsa_createctx, + pkcs11eddsa_destroyctx, + pkcs11eddsa_adddata, + pkcs11eddsa_sign, + pkcs11eddsa_verify, + NULL, /*%< verify2 */ + NULL, /*%< computesecret */ + pkcs11eddsa_compare, + NULL, /*%< paramcompare */ + pkcs11eddsa_generate, + pkcs11eddsa_isprivate, + pkcs11eddsa_destroy, + pkcs11eddsa_todns, + pkcs11eddsa_fromdns, + pkcs11eddsa_tofile, + pkcs11eddsa_parse, + NULL, /*%< cleanup */ + pkcs11eddsa_fromlabel, + NULL, /*%< dump */ + NULL, /*%< restore */ +}; + +isc_result_t +dst__pkcs11eddsa_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) + *funcp = &pkcs11eddsa_functions; + return (ISC_R_SUCCESS); +} + +#else /* PKCS11CRYPTO && HAVE_PKCS11_EDxxx */ + +#include + +EMPTY_TRANSLATION_UNIT + +#endif /* PKCS11CRYPTO && HAVE_PKCS11_EDxxx */ +/*! \file */ diff --git a/lib/export/dns/Makefile.in b/lib/export/dns/Makefile.in index d59f15973b..25f5581b9f 100644 --- a/lib/export/dns/Makefile.in +++ b/lib/export/dns/Makefile.in @@ -42,8 +42,10 @@ LIBS = @LIBS@ # Alphabetically OPENSSLGOSTLINKOBJS = opensslgost_link.@O@ +OPENSSLEDDSALINKOBJS = openssleddsa_link.@O@ OPENSSLLINKOBJS = openssl_link.@O@ openssldh_link.@O@ openssldsa_link.@O@ \ - opensslecdsa_link.@O@ @OPENSSLGOSTLINKOBJS@ opensslrsa_link.@O@ + opensslecdsa_link.@O@ @OPENSSLEDDSALINKOBJS@ \ + @OPENSSLGOSTLINKOBJS@ opensslrsa_link.@O@ DSTOBJS = @OPENSSLLINKOBJS@ \ dst_api.@O@ dst_lib.@O@ dst_parse.@O@ dst_result.@O@ \ @@ -71,8 +73,10 @@ OBJS= ${DNSOBJS} ${OTHEROBJS} ${DSTOBJS} ${PORTDNSOBJS} # Alphabetically OPENSSLGOSTLINKSRCS = opensslgost_link.c +OPENSSLEDDDSALINKSRCS = openssleddsa_link.c OPENSSLLINKSRCS = openssl_link.c openssldh_link.c openssldsa_link.c \ - opensslecdsa_link.c @OPENSSLGOSTLINKSRCS@ opensslrsa_link.c + opensslecdsa_link.c @OPENSSLEDDSALINKSRCS@ @OPENSSLGOSTLINKSRCS@ \ + opensslrsa_link.c DSTSRCS = @OPENSSLLINKSRCS@ \ dst_api.c dst_lib.c dst_parse.c \