Commit 0b60906a authored by Daniel Salzman's avatar Daniel Salzman

Merge branch 'dnssec-hsm' into 'master'

DNSSEC PKCS11 support

See merge request !470
parents b18f769a 7c204b5e
......@@ -128,6 +128,14 @@ PKG_CHECK_MODULES([gnutls], [gnutls >= 3.0 nettle], [
CFLAGS="$CFLAGS $gnutls_CFLAGS"
AC_CHECK_HEADERS([nettle/version.h])
CFLAGS=$CFLAGS_save
LDFLAGS_save=$LDFLAGS
LDFLAGS="$LDFLAGS $gnutls_LIBS"
AC_CHECK_FUNC([gnutls_pkcs11_privkey_generate3], [enable_pkcs11=yes], [enable_pkcs11=no])
AS_IF([test "$enable_pkcs11" = yes],
[AC_DEFINE([ENABLE_PKCS11], [1], [PKCS 11 support available])])
LDFLAGS=$LDFLAGS_save
])
# JSON for DNSSEC status storage
......@@ -549,6 +557,7 @@ AC_MSG_RESULT([
Dnstap support: ${opt_dnstap}
Code coverage: ${enable_code_coverage}
Bash completions: ${bash_completions_output}
PKCS 11 support: ${enable_pkcs11}
Continue with 'make' command
])
......@@ -304,13 +304,13 @@ default template, but the signing is explicitly disabled for zone
DNSSEC KASP database
--------------------
The configuration for DNSSEC is stored in a :abbr:`KASP (Key And Signature
The configuration for DNSSEC is stored in the :abbr:`KASP (Key And Signature
Policy)` database. The database is simply a directory in the file-system
containing files in the JSON format. The database contains
- definitions of signing policies;
- zones configuration; and
- private key material.
- private key stores configuration; and
- zones configuration and signing metadata.
The :doc:`keymgr <man_keymgr>` utility serves for the database maintenance.
To initialize the database, run:
......@@ -321,10 +321,15 @@ To initialize the database, run:
$ cd /var/lib/knot/kasp
$ keymgr init
The *init* command initializes the database, defines a default signing policy
named *default* with default signing parameters, and defines a default key
store named *default* with file-backed key store within the KASP database
directory.
.. ATTENTION::
Make sure to set the KASP database permissions correctly. For manual key
management, the database must be **readable** by the server process. For
automatic key management, it must be **writeable**. The database also
management, the database must be *readable* by the server process. For
automatic key management, it must be *writeable*. The database also
contains private key material – don't set the permissions too loose.
.. _dnssec-automatic-key-management:
......@@ -336,12 +341,12 @@ For automatic key management, a signing policy has to be defined in the
first place. This policy specifies how a zone is signed (i.e. signing
algorithm, key size, signature lifetime, key lifetime, etc.).
To create a new policy named *default_rsa* using *RSA-SHA-256* algorithm for
To create a new policy named *rsa* using *RSA-SHA-256* algorithm for
signing keys, 1024-bit long ZSK, and 2048-bit long KSK, run:
.. code-block:: console
$ keymgr policy add default_rsa algorithm RSASHA256 zsk-size 1024 ksk-size 2048
$ keymgr policy add rsa algorithm RSASHA256 zsk-size 1024 ksk-size 2048
The unspecified policy parameters are set to defaults. The complete definition
of the policy will be printed after executing the command.
......@@ -351,13 +356,13 @@ created policy:
.. code-block:: console
$ keymgr zone add myzone.test policy default_rsa
$ keymgr zone add myzone.test policy rsa
Make sure everything is set correctly:
.. code-block:: console
$ keymgr policy show default_rsa
$ keymgr policy show rsa
$ keymgr zone show myzone.test
Add the zone into the server configuration and enable DNSSEC for that zone.
......@@ -394,17 +399,23 @@ Manual key management
---------------------
For automatic DNSSEC signing with manual key management, a signing policy
need not be defined.
with manual key management flag has to be set.
Define a signing policy named *man* with disabled automatic key management:
.. code-block:: console
$ keymgr policy add man manual true
Create a zone entry for the zone *myzone.test* without a policy:
Create a zone entry for the zone *myzone.test* with the created policy:
.. code-block:: console
$ keymgr zone add myzone.test
$ keymgr zone add myzone.test policy man
Generate signing keys for the zone. Let's use the Single-Type Signing scheme
with two algorithms (this scheme is not supported in automatic key management).
Run:
with two algorithms, which is a scheme currently not supported by the automatic
key management. Run:
.. code-block:: console
......@@ -452,14 +463,14 @@ of the following parameters:
Signing algorithm
An algorithm of signing keys and issued signatures. The default value is
*RSA-SHA-256*.
*ECDSA-P256-SHA256*.
:abbr:`KSK (Key Signing Key)` size
Desired length of the newly generated ZSK keys. The default value is 2048
bits.
Desired length of the newly generated ZSK keys. The default value is 256
bits (the only feasible value for the default signing algorithm).
:abbr:`ZSK (Zone Signing Key)` size
Desired length of the newly generated ZSK keys. The default value is 1024
Desired length of the newly generated ZSK keys. The default value is 256
bits.
DNSKEY TTL
......@@ -497,6 +508,14 @@ Propagation delay
enough to cover propagation of data from the master server to all slaves.
The default value is 1 hour.
Key store name
A name of a key store holding private key material for zones which use the
policy. The default value is *default*.
Manual key management
An option to disable key management for all zones which use the policy. The
option is disabled by default.
.. _dnssec-signing:
Zone signing
......
......@@ -67,7 +67,8 @@ directory.
.INDENT 0.0
.TP
\fBinit\fP
Initialize new KASP database.
Initialize new KASP database. This also creates a default policy and default
key store (both named \fIdefault\fP).
.TP
\fBzone\fP ...
Operations with zones in the database. A zone holds assigned signing
......@@ -78,8 +79,9 @@ Operations with KASP policies. A policy holds parameters that define the
way how a zone is signed.
.TP
\fBkeystore\fP ...
Operations with private key store content. The private key store holds
private key material separately from zone metadata.
Operations with key stores configured for the KASP database. A private key
store holds private key material for zone signing separately from the zone
metadata.
.TP
\fBtsig\fP ...
Operations with TSIG keys.
......@@ -221,17 +223,55 @@ Max TTL in the zone.
\fBdelay\fP \fIseconds\fP
Zone signing and data propagation delay. The value is added for safety to
timing of all rollover steps.
.TP
\fBmanual\fP \fIenable\fP
Enable manual key management. If enabled, no keys will be generated or
rolled automatically.
.TP
\fBkeystore\fP \fIname\fP
Name of the key store to be used for private key material.
.UNINDENT
.UNINDENT
.UNINDENT
.SS keystore commands
.sp
The key store functionality is limited at the moment. Only one instance of
file\-based key store is supported. This command is subject to change.
.INDENT 0.0
.TP
\fBkeystore\fP \fBlist\fP
List private keys in the key store.
List names of configured key stores.
.TP
\fBkeystore\fP \fBshow\fP \fIname\fP
Show configuration of a key store named \fIname\fP and list key IDs of private
key material present in that key store.
.TP
\fBkeystore\fP \fBadd\fP \fIname\fP [\fBbackend\fP \fIbackend\fP] [\fBconfig\fP \fIconfig\fP]
Configure new key store. The \fIname\fP is a unique key store identifier. The
\fIbackend\fP and backend\-specific configuration string \fIconfig\fP determine where
the private key material will be physically stored.
.UNINDENT
.sp
Supported key store backends:
.INDENT 0.0
.INDENT 3.5
.INDENT 0.0
.TP
\fBpkcs8\fP (default)
The backend stores private key material in unencrypted X.509 PEM files
in a directory specified as the backend configuration string. The path
can be specified relatively to the KASP database location.
.TP
\fBpkcs11\fP
The backend stores private key material in a cryptographic token accessible
via the PKCS #11 interface. The configuration string consists of a token
PKCS #11 URL and PKCS #11 module path separated by the space character.
.sp
The format of the PKCS #11 URL is described in \fI\%RFC 7512\fP\&. If the token
is protected by a PIN, make sure to include \fIpin\-value\fP or \fIpin\-source\fP
attribute in the URL.
.sp
The PKCS #11 module path can be an absolute path or just a module name. In
the later case, the module is looked up in the default modules location.
.UNINDENT
.UNINDENT
.UNINDENT
.SS tsig commands
.INDENT 0.0
......@@ -249,9 +289,8 @@ utilities.
.SH EXAMPLES
.INDENT 0.0
.IP 1. 3
Initialize a new KASP database, add a policy named \fIdefault\fP with default
parameters, and add a zone \fIexample.com\fP\&. The zone will use the created
policy:
Initialize a new KASP database and add a zone \fIexample.com\fP with the
\fIdefault\fP policy assigned:
.INDENT 3.0
.INDENT 3.5
.sp
......@@ -284,8 +323,8 @@ existing zone:
.sp
.nf
.ft C
$ keymgr policy add lab rrsig\-lifetime 300 rrsig\-refresh 150 zsk\-lifetime 600 \e
delay 10
$ keymgr policy add lab rrsig\-lifetime 300 rrsig\-refresh 150 \e
zsk\-lifetime 600 delay 10
$ keymgr zone set example.com policy lab
.ft P
.fi
......@@ -361,6 +400,23 @@ $ keymgr tsig generate operator.key algorithm hmac\-sha512
.fi
.UNINDENT
.UNINDENT
.IP 9. 3
Add a new key store named \fIhsm\fP and backed by the SoftHSM PKCS #11 module,
then add a new policy named \fIsecure\fP with default parameters using this key
store, and finally add the zone \fIexample.com\fP which will use this policy:
.INDENT 3.0
.INDENT 3.5
.sp
.nf
.ft C
$ keymgr keystore add hsm backend pkcs11 \e
config "pkcs11:token=knot;pin\-value=1234 libsofthsm2.so"
$ keymgr policy add secure keystore hsm
$ keymgr zone add example.com policy secure
.ft P
.fi
.UNINDENT
.UNINDENT
.UNINDENT
.SH SEE ALSO
.sp
......
......@@ -44,7 +44,8 @@ Main commands
.............
**init**
Initialize new KASP database.
Initialize new KASP database. This also creates a default policy and default
key store (both named *default*).
**zone** ...
Operations with zones in the database. A zone holds assigned signing
......@@ -55,8 +56,9 @@ Main commands
way how a zone is signed.
**keystore** ...
Operations with private key store content. The private key store holds
private key material separately from zone metadata.
Operations with key stores configured for the KASP database. A private key
store holds private key material for zone signing separately from the zone
metadata.
**tsig** ...
Operations with TSIG keys.
......@@ -189,14 +191,46 @@ Available *policy-parameter*\ s:
Zone signing and data propagation delay. The value is added for safety to
timing of all rollover steps.
**manual** *enable*
Enable manual key management. If enabled, no keys will be generated or
rolled automatically.
**keystore** *name*
Name of the key store to be used for private key material.
keystore commands
.................
The key store functionality is limited at the moment. Only one instance of
file-based key store is supported. This command is subject to change.
**keystore** **list**
List private keys in the key store.
List names of configured key stores.
**keystore** **show** *name*
Show configuration of a key store named *name* and list key IDs of private
key material present in that key store.
**keystore** **add** *name* [**backend** *backend*] [**config** *config*]
Configure new key store. The *name* is a unique key store identifier. The
*backend* and backend-specific configuration string *config* determine where
the private key material will be physically stored.
Supported key store backends:
**pkcs8** (default)
The backend stores private key material in unencrypted X.509 PEM files
in a directory specified as the backend configuration string. The path
can be specified relatively to the KASP database location.
**pkcs11**
The backend stores private key material in a cryptographic token accessible
via the PKCS #11 interface. The configuration string consists of a token
PKCS #11 URL and PKCS #11 module path separated by the space character.
The format of the PKCS #11 URL is described in :rfc:`7512`. If the token
is protected by a PIN, make sure to include *pin-value* or *pin-source*
attribute in the URL.
The PKCS #11 module path can be an absolute path or just a module name. In
the later case, the module is looked up in the default modules location.
tsig commands
.............
......@@ -214,9 +248,8 @@ tsig commands
Examples
--------
1. Initialize a new KASP database, add a policy named *default* with default
parameters, and add a zone *example.com*. The zone will use the created
policy::
1. Initialize a new KASP database and add a zone *example.com* with the
*default* policy assigned::
$ keymgr init
$ keymgr policy add default
......@@ -229,8 +262,8 @@ Examples
3. Add a testing policy *lab* with rapid key rollovers. Apply the policy to an
existing zone::
$ keymgr policy add lab rrsig-lifetime 300 rrsig-refresh 150 zsk-lifetime 600 \
delay 10
$ keymgr policy add lab rrsig-lifetime 300 rrsig-refresh 150 \
zsk-lifetime 600 delay 10
$ keymgr zone set example.com policy lab
4. Add an existing and already secured zone. Let the keys be managed by the
......@@ -263,6 +296,15 @@ Examples
$ keymgr tsig generate operator.key algorithm hmac-sha512
9. Add a new key store named *hsm* and backed by the SoftHSM PKCS #11 module,
then add a new policy named *secure* with default parameters using this key
store, and finally add the zone *example.com* which will use this policy::
$ keymgr keystore add hsm backend pkcs11 \
config "pkcs11:token=knot;pin-value=1234 libsofthsm2.so"
$ keymgr policy add secure keystore hsm
$ keymgr zone add example.com policy secure
See Also
--------
......
libtap_la_SOURCES = \
tap/basic.c tap/basic.h \
tap/files.c tap/files.h \
tap/float.c tap/float.h \
tap/macros.h
......
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "files.h"
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
char *test_mkdtemp(void)
{
char *tmpdir = getenv("TMPDIR");
if (!tmpdir) {
tmpdir = "/tmp";
}
char tmp[4096] = { 0 };
int r = snprintf(tmp, sizeof(tmp), "%s/knot_unit.XXXXXX", tmpdir);
if (r <= 0 || r >= sizeof(tmp)) {
return NULL;
}
if (mkdtemp(tmp) == NULL) {
return NULL;
}
return strdup(tmp);
}
static bool special_name(const char *name)
{
return strcmp(name, ".") == 0 || strcmp(name, "..") == 0;
}
static bool rm_dir_contents(int dir_fd)
{
DIR *dir = fdopendir(dir_fd);
if (!dir) {
return false;
}
bool success = true;
struct dirent entry = { 0 };
struct dirent *result = NULL;
while (success && readdir_r(dir, &entry, &result) == 0 && result) {
if (special_name(result->d_name)) {
continue;
}
bool is_dir = result->d_type == DT_DIR;
if (is_dir) {
int sub = openat(dir_fd, result->d_name, O_NOFOLLOW);
success = rm_dir_contents(sub);
close(sub);
}
if (success) {
int flags = is_dir ? AT_REMOVEDIR : 0;
success = unlinkat(dir_fd, result->d_name, flags) == 0;
}
}
closedir(dir);
return success;
}
bool test_rm_rf(const char *path)
{
int fd = open(path, O_NOFOLLOW);
if (fd < 0) {
return false;
}
struct stat st = { 0 };
if (fstat(fd, &st) != 0) {
close(fd);
return false;
}
if (S_ISDIR(st.st_mode) && !rm_dir_contents(fd)) {
close(fd);
return false;
}
close(fd);
return (remove(path) == 0);
}
/* Copyright (C) 2015 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdbool.h>
/*!
* \brief Create temporary directory.
*
* If TMPDIR environment variable is set, the directory is created within
* that directory. If the variable is not set, the directory is created
* within /tmp.
*/
char *test_mkdtemp(void);
/*!
* \brief Delete file or directory (recursive).
*/
bool test_rm_rf(const char *path);
......@@ -30,8 +30,8 @@ libshared_la_SOURCES = \
shared/fs.h \
shared/hex.c \
shared/hex.h \
shared/hex_gnutls.c \
shared/hex_gnutls.h \
shared/keyid_gnutls.c \
shared/keyid_gnutls.h \
shared/path.c \
shared/path.h \
shared/pem.c \
......@@ -77,9 +77,9 @@ libdnssec_la_SOURCES = \
lib/binary.c \
lib/crypto.c \
lib/error.c \
lib/event/action.h \
lib/event/action/initial_key.c \
lib/event/action/zsk_rollover.c \
lib/event/action.h \
lib/event/event.c \
lib/event/keystate.c \
lib/event/keystate.h \
......@@ -92,14 +92,18 @@ libdnssec_la_SOURCES = \
lib/kasp/dir/file.h \
lib/kasp/dir/json.c \
lib/kasp/dir/json.h \
lib/kasp/dir/zone.c \
lib/kasp/dir/zone.h \
lib/kasp/dir/keystore.c \
lib/kasp/dir/keystore.h \
lib/kasp/dir/policy.c \
lib/kasp/dir/policy.h \
lib/kasp/dir/zone.c \
lib/kasp/dir/zone.h \
lib/kasp/internal.h \
lib/kasp/kasp.c \
lib/kasp/zone.c \
lib/kasp/keystore.c \
lib/kasp/keystore_open.c \
lib/kasp/policy.c \
lib/kasp/zone.c \
lib/kasp/zone.h \
lib/key/algorithm.c \
lib/key/algorithm.h \
......
......@@ -23,7 +23,9 @@
_public_
void dnssec_crypto_init(void)
{
//gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
#ifdef ENABLE_PKCS11
gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
#endif
gnutls_global_init();
}
......@@ -31,11 +33,15 @@ _public_
void dnssec_crypto_cleanup(void)
{
gnutls_global_deinit();
//gnutls_pkcs11_deinit();
#ifdef ENABLE_PKCS11
gnutls_pkcs11_deinit();
#endif
}
_public_
void dnssec_crypto_reinit(void)
{
//gnutls_pkcs11_reinit();
#ifdef ENABLE_PKCS11
gnutls_pkcs11_reinit();
#endif
}
......@@ -89,6 +89,10 @@ enum dnssec_error {
DNSSEC_CONFIG_MALFORMED,
DNSSEC_CONFIG_INVALID_KEY_ID,
DNSSEC_KEYSTORE_INVALID_BACKEND,
DNSSEC_KEYSTORE_INVALID_CONFIG,
DNSSEC_KEYSTORE_FAILED_TO_LOAD_P11_MODULE,
DNSSEC_ERROR_MAX = -1001
};
......
......@@ -222,6 +222,7 @@ typedef struct dnssec_kasp_key_timing {
* Zone key.
*/
typedef struct dnssec_kasp_key {
char *id; /*!< Keystore unique key ID. */
dnssec_key_t *key; /*!< Instance of the key. */
dnssec_kasp_key_timing_t timing; /*!< Key timing information. */
} dnssec_kasp_key_t;
......@@ -278,6 +279,8 @@ int dnssec_kasp_zone_set_policy(dnssec_kasp_zone_t *zone, const char *name);
*/
typedef struct dnssec_kasp_policy {
char *name;
bool manual;
char *keystore;
// DNSKEY
dnssec_key_algorithm_t algorithm;
uint16_t ksk_size;
......@@ -366,4 +369,33 @@ int dnssec_kasp_policy_list(dnssec_kasp_t *kasp, dnssec_list_t **list);
*/
int dnssec_kasp_policy_exists(dnssec_kasp_t *kasp, const char *policy_name);
typedef struct dnssec_kasp_keystore {
char *name;
char *backend;
char *config;
} dnssec_kasp_keystore_t;
dnssec_kasp_keystore_t *dnssec_kasp_keystore_new(const char *name);
void dnssec_kasp_keystore_free(dnssec_kasp_keystore_t *keystore);
int dnssec_kasp_keystore_load(dnssec_kasp_t *kasp, const char *name, dnssec_kasp_keystore_t **keystore);
int dnssec_kasp_keystore_save(dnssec_kasp_t *kasp, dnssec_kasp_keystore_t *keystore);
int dnssec_kasp_keystore_remove(dnssec_kasp_t *kasp, const char *name);
int dnssec_kasp_keystore_list(dnssec_kasp_t *kasp, dnssec_list_t **names);
int dnssec_kasp_keystore_exists(dnssec_kasp_t *kasp, const char *name);
/*
* TODO: workaround, PKCS 8 dir keystore needs to know KASP base path
*/
#define DNSSEC_KASP_KEYSTORE_PKCS8 "pkcs8"
#define DNSSEC_KASP_KEYSTORE_PKCS11 "pkcs11"
typedef struct dnssec_keystore dnssec_keystore_t;
int dnssec_kasp_keystore_init(dnssec_kasp_t *kasp, const char *backend,
const char *config, dnssec_keystore_t **store);
int dnssec_kasp_keystore_open(dnssec_kasp_t *kasp, const char *backend,
const char *config, dnssec_keystore_t **store);
/*! @} */
......@@ -53,9 +53,8 @@
* return result;
* }
*
* // print key ID and key tag.
* printf("key %s (%s)\n", dnssec_key_get_id(key),
* dnssec_key_get_keytag(key));
* // print key tag
* printf("key %s\n", dnssec_key_get_keytag(key));
*
* // make sure what we can do with the key
* assert(dnssec_key_can_verify(key) == true);
......@@ -77,7 +76,6 @@
#include <stdint.h>
#include <dnssec/binary.h>
#include <dnssec/keyid.h>
/*!
* DNSKEY algorithm numbers.
......@@ -137,11 +135,6 @@ dnssec_key_t *dnssec_key_dup(const dnssec_key_t *key);
*/
uint16_t dnssec_key_get_keytag(const dnssec_key_t *key);
/*!
* Get the key ID of the DNSSEC key.
*/
const char *dnssec_key_get_id(const dnssec_key_t *key);
/*!
* Get the domain name of the DNSSEC key.
*/
......
......@@ -202,8 +202,6 @@ int dnssec_keystore_init_pkcs8_custom(dnssec_keystore_t **store,
/*!
* Crate new PKCS #11 private key store context.
*
* \todo Not implemented.
*
* \param[out] store Opened key store.
*
* \return Error code, DNSSEC_EOK if successful.
......@@ -285,24 +283,17 @@ int dnssec_keystore_import(dnssec_keystore_t *store, const dnssec_binary_t *pem,
int dnssec_keystore_remove_key(dnssec_keystore_t *store, const char *id);
/*!
* Import public and private key from the key store into a DNSSEC key.
* Import public and/or private key from the key store into a DNSSEC key.
*
* \param key DNSSEC key to be initialized.
* \param keystore Private key store.
* \param id ID of the key.
* \param algorithm Algorithm of the key.
* The key algorithm has to be set before calling this function.
*
* \param key DNSSEC key to be initialized.
* \param keystore Private key store.
* \param id ID of the key.
*
* \return Error code, DNSSEC_EOK if successful.
*/
int dnssec_key_import_keystore(dnssec_key_t *key, dnssec_keystore_t *keystore,
const char *id, dnssec_key_algorithm_t algorithm);
/*!
* Import private key from the key store into a DNSSEC key.
*
* \param key DNSSEC key, the public key must be already loaded.
* \param keystore Private key store.
*/
int dnssec_key_import_private_keystore(dnssec_key_t *key, dnssec_keystore_t *keystore);
const char *id);
/*! @} */
......@@ -66,6 +66,10 @@ static const error_message_t ERROR_MESSAGES[] = {
{ DNSSEC_CONFIG_MALFORMED, "malformed config value" },
{ DNSSEC_CONFIG_INVALID_KEY_ID, "invalid key ID in config" },
{ DNSSEC_KEYSTORE_INVALID_BACKEND, "invalid KASP keystore backend" },
{ DNSSEC_KEYSTORE_INVALID_CONFIG, "invalid KASP keystore configuration" },
{ DNSSEC_KEYSTORE_FAILED_TO_LOAD_P11_MODULE, "failed to load PKCS #11 module" },