diff options
Diffstat (limited to 'src/utils')
-rw-r--r-- | src/utils/Makefile.inc | 2 | ||||
-rw-r--r-- | src/utils/keymgr/keystore.c | 395 | ||||
-rw-r--r-- | src/utils/keymgr/keystore.h | 24 | ||||
-rw-r--r-- | src/utils/keymgr/main.c | 39 | ||||
-rw-r--r-- | src/utils/kzonecheck/main.c | 4 | ||||
-rw-r--r-- | src/utils/kzonecheck/zone_check.c | 4 | ||||
-rw-r--r-- | src/utils/kzonecheck/zone_check.h | 2 |
7 files changed, 463 insertions, 7 deletions
diff --git a/src/utils/Makefile.inc b/src/utils/Makefile.inc index b39b10d..3097050 100644 --- a/src/utils/Makefile.inc +++ b/src/utils/Makefile.inc @@ -159,6 +159,8 @@ keymgr_SOURCES = \ utils/keymgr/bind_privkey.h \ utils/keymgr/functions.c \ utils/keymgr/functions.h \ + utils/keymgr/keystore.c \ + utils/keymgr/keystore.h \ utils/keymgr/offline_ksk.c \ utils/keymgr/offline_ksk.h \ utils/keymgr/main.c diff --git a/src/utils/keymgr/keystore.c b/src/utils/keymgr/keystore.c new file mode 100644 index 0000000..a4a0937 --- /dev/null +++ b/src/utils/keymgr/keystore.c @@ -0,0 +1,395 @@ +/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. + */ + +#include <stdio.h> + +#include "utils/keymgr/keystore.h" + +#include "contrib/color.h" +#include "contrib/spinlock.h" +#include "contrib/time.h" +#include "libdnssec/error.h" +#include "libdnssec/key/algorithm.h" +#include "libdnssec/key/privkey.h" +#include "libdnssec/random.h" +#include "libdnssec/sample_keys.h" +#include "libdnssec/sign.h" +#include "libknot/errcode.h" +#include "knot/conf/conf.h" +#include "knot/dnssec/kasp/kasp_zone.h" +#include "knot/server/dthreads.h" +#include "utils/common/msg.h" + +#define DFLT_ID "-" + +#define TEST_FORMAT "%-18s %9s %9s %9s %9s\n" +#define BENCH_FORMAT "%-18s %9" +#define BENCH_TIME 3000 + +static const key_parameters_t *KEYS[] = { + &SAMPLE_RSA_KEY, + &SAMPLE_ECDSA_KEY, + &SAMPLE_ED25519_KEY, + &SAMPLE_ED448_KEY, +}; +static const int KEYS_COUNT = sizeof(KEYS) / sizeof(*KEYS); + +static int create_dnskeys(dnssec_keystore_t *keystore, const char *id, + dnssec_key_algorithm_t algorithm, + dnssec_key_t **test_key_ptr, dnssec_key_t **ref_key_ptr) +{ + dnssec_key_t *test_key = NULL; + if (dnssec_key_new(&test_key) != DNSSEC_EOK || + dnssec_key_set_algorithm(test_key, algorithm) != DNSSEC_EOK || + dnssec_keystore_get_private(keystore, id, test_key) != DNSSEC_EOK) { + dnssec_key_free(test_key); + return KNOT_ERROR; + } + + dnssec_binary_t rdata; + dnssec_key_t *ref_key = NULL; + if (dnssec_key_new(&ref_key) != DNSSEC_EOK || + dnssec_key_get_rdata(test_key, &rdata) != DNSSEC_EOK || + dnssec_key_set_rdata(ref_key, &rdata) != DNSSEC_EOK) { + dnssec_key_free(test_key); + dnssec_key_free(ref_key); + return KNOT_ERROR; + } + + *test_key_ptr = test_key; + *ref_key_ptr = ref_key; + + return KNOT_EOK; +} + +static int test_sign(dnssec_key_t *test_key, dnssec_key_t *ref_key) +{ + static const dnssec_binary_t input = { + .data = (uint8_t *)"WuSEFCiFEKDTKuErihBW76q7p70dHuCfS6c1ffCK6ST", + .size = 43 + }; + + dnssec_binary_t sign = { 0 }; + + dnssec_sign_ctx_t *ctx = NULL; + if (dnssec_sign_new(&ctx, test_key) != DNSSEC_EOK || + dnssec_sign_add(ctx, &input) != DNSSEC_EOK || + dnssec_sign_write(ctx, DNSSEC_SIGN_NORMAL, &sign) != DNSSEC_EOK) { + dnssec_binary_free(&sign); + dnssec_sign_free(ctx); + return KNOT_ERROR; + } + + if (dnssec_sign_init(ctx) != DNSSEC_EOK || + dnssec_sign_add(ctx, &input) != DNSSEC_EOK || + dnssec_sign_verify(ctx, false, &sign) != DNSSEC_EOK) { + dnssec_binary_free(&sign); + dnssec_sign_free(ctx); + return KNOT_ERROR; + } + + dnssec_sign_free(ctx); + ctx = NULL; + + if (dnssec_sign_new(&ctx, ref_key) != DNSSEC_EOK || + dnssec_sign_add(ctx, &input) != DNSSEC_EOK || + dnssec_sign_verify(ctx, false, &sign) != DNSSEC_EOK) { + dnssec_binary_free(&sign); + dnssec_sign_free(ctx); + return KNOT_ERROR; + } + + dnssec_binary_free(&sign); + dnssec_sign_free(ctx); + + return KNOT_EOK; +} + +static int test_key_use(dnssec_keystore_t *store, const char *keyid, + dnssec_key_algorithm_t algorithm) +{ + dnssec_key_t *test_key = NULL; + dnssec_key_t *ref_key = NULL; + + if (create_dnskeys(store, keyid, algorithm, &test_key, &ref_key) != KNOT_EOK) { + return KNOT_ERROR; + } + + if (test_sign(test_key, ref_key) != KNOT_EOK) { + dnssec_key_free(test_key); + dnssec_key_free(ref_key); + return KNOT_ERROR; + } + + dnssec_key_free(test_key); + dnssec_key_free(ref_key); + + return KNOT_EOK; +} + +static void test_algorithm(dnssec_keystore_t *store, + const key_parameters_t *params) +{ + struct { + bool generate; + bool import; + bool remove; + bool use; + } res = { 0 }; + + char *id = NULL; + int ret = dnssec_keystore_generate(store, params->algorithm, + params->bit_size, NULL, &id); + if (ret == DNSSEC_EOK) { + res.generate = true; + + ret = test_key_use(store, id, params->algorithm); + res.use = (ret == KNOT_EOK); + + ret = dnssec_keystore_remove(store, id); + res.remove = (ret == DNSSEC_EOK); + free(id); + } + + ret = dnssec_keystore_import(store, ¶ms->pem, &id); + if (ret == DNSSEC_EOK) { + res.import = true; + + ret = test_key_use(store, id, params->algorithm); + if (res.generate) { + res.use &= (ret == KNOT_EOK); + } else { + res.use = (ret == KNOT_EOK); + } + + ret = dnssec_keystore_remove(store, id); + if (res.generate) { + res.remove &= (ret == DNSSEC_EOK); + } else { + res.remove = (ret == DNSSEC_EOK); + } + free(id); + } + + const knot_lookup_t *alg_info = knot_lookup_by_id(knot_dnssec_alg_names, + params->algorithm); + assert(alg_info); + + printf(TEST_FORMAT, + alg_info->name, + res.generate ? "yes" : "no", + res.import ? "yes" : "no", + res.remove ? "yes" : "no", + res.use ? "yes" : "no"); +} + +static int init_keystore(dnssec_keystore_t **store, const char *keystore_id, + unsigned threads) +{ + size_t len = strlen(keystore_id) + 1; + conf_val_t id = conf_rawid_get(conf(), C_KEYSTORE, C_ID, + (const uint8_t *)keystore_id, len); + if (id.code != KNOT_EOK && strcmp(keystore_id, DFLT_ID) != 0) { + ERR2("keystore '%s' not configured", keystore_id); + return id.code; + } + id.blob = (const uint8_t *)keystore_id; + id.blob_len = len; + + unsigned backend; + bool key_label; + + int ret = zone_init_keystore(conf(), NULL, &id, store, &backend, &key_label); + if (ret != KNOT_EOK) { + ERR2("failed to open '%s' keystore (%s)", keystore_id, knot_strerror(ret)); + return ret; + } + + if (strcmp(keystore_id, DFLT_ID) == 0) { + printf("Keystore default"); + } else { + printf("Keystore id '%s'", keystore_id); + }; + printf(", type %s", (backend == KEYSTORE_BACKEND_PEM ? "PEM" : "PKCS #11")); + if (threads > 0) { + printf(", threads %u", threads); + } + printf("\n\n"); + + return KNOT_EOK; +} + +int keymgr_keystore_test(const char *keystore_id, keymgr_list_params_t *params) +{ + dnssec_keystore_t *store = NULL; + + int ret = init_keystore(&store, keystore_id, 0); + if (ret != KNOT_EOK) { + goto done; + } + + const bool c = params->color; + printf("%s" TEST_FORMAT "%s", + COL_UNDR(c), + "Algorithm", "Generate", "Import", "Remove", "Use", + COL_RST(c)); + for (int i = 0; i < KEYS_COUNT; i++) { + test_algorithm(store, KEYS[i]); + } +done: + dnssec_keystore_deinit(store); + + return ret; +} + +struct result { + unsigned long signs; + unsigned long time; +}; + +typedef struct bench_ctx { + dnssec_keystore_t *store; + const key_parameters_t *params; + struct result *results; + knot_spin_t lock; +} bench_ctx_t; + +static int bench(dthread_t *dt) +{ + assert(dt != NULL && dt->data != NULL); + + bench_ctx_t *data = dt->data; + dnssec_keystore_t *store = data->store; + const key_parameters_t *params = data->params; + struct result *result = data->results + dt_get_id(dt); + + result->time = 0; + result->signs = 0; + + char *id = NULL; + dnssec_key_t *test_key = NULL; + knot_spin_lock(&data->lock); + int ret = dnssec_keystore_generate(store, params->algorithm, + params->bit_size, NULL, &id); + if (ret != DNSSEC_EOK || + dnssec_key_new(&test_key) != DNSSEC_EOK || + dnssec_key_set_algorithm(test_key, params->algorithm) != DNSSEC_EOK || + dnssec_keystore_get_private(store, id, test_key) != DNSSEC_EOK) { + goto finish; + } + knot_spin_unlock(&data->lock); + + uint8_t input_data[64]; + dnssec_binary_t input = { + .data = input_data, + .size = sizeof(input_data) + }; + (void)dnssec_random_binary(&input); + + struct timespec start_ts, end_ts; + clock_gettime(CLOCK_MONOTONIC, &start_ts); + + while (result->time < BENCH_TIME) { + dnssec_binary_t sign = { 0 }; + dnssec_sign_ctx_t *ctx = NULL; + if (dnssec_sign_new(&ctx, test_key) != DNSSEC_EOK || + dnssec_sign_add(ctx, &input) != DNSSEC_EOK || + dnssec_sign_write(ctx, DNSSEC_SIGN_NORMAL, &sign) != DNSSEC_EOK) { + dnssec_binary_free(&sign); + dnssec_sign_free(ctx); + result->time = 0; + goto finish; + } + memcpy(input.data, sign.data, MIN(input.size, sign.size)); + dnssec_binary_free(&sign); + dnssec_sign_free(ctx); + + clock_gettime(CLOCK_MONOTONIC, &end_ts); + result->time = time_diff_ms(&start_ts, &end_ts); + result->signs++; + } + +finish: + knot_spin_unlock(&data->lock); + dnssec_key_free(test_key); + (void)dnssec_keystore_remove(store, id); + free(id); + + return KNOT_EOK; +} + +int keymgr_keystore_bench(const char *keystore_id, keymgr_list_params_t *params, + uint16_t threads) +{ + dnssec_keystore_t *store = NULL; + + int ret = init_keystore(&store, keystore_id, threads); + if (ret != KNOT_EOK) { + return ret; + } + + const bool c = params->color; + printf("%s" BENCH_FORMAT"s\n" "%s", + COL_UNDR(c), + "Algorithm", "Sigs/sec", + COL_RST(c)); + + for (int i = 0; i < KEYS_COUNT; i++) { + struct result results[threads]; + bench_ctx_t d = { + .store = store, + .params = KEYS[i], + .results = results + }; + knot_spin_init(&d.lock); + + dt_unit_t *pool = dt_create(threads, bench, NULL, &d); + if (pool == NULL || + dt_start(pool) != KNOT_EOK || + dt_join(pool) != KNOT_EOK) { + dt_delete(&pool); + knot_spin_destroy(&d.lock); + dnssec_keystore_deinit(store); + return KNOT_ERROR; + } + dt_delete(&pool); + knot_spin_destroy(&d.lock); + + double result_f = 0.5; // 0.5 to ensure correct rounding + for (struct result *it = d.results; it < d.results + threads; ++it) { + if (it->time == 0) { + result_f = 0.; + break; + } + result_f += it->signs * 1000. / it->time; + } + + const knot_lookup_t *alg_info = knot_lookup_by_id( + knot_dnssec_alg_names, KEYS[i]->algorithm); + assert(alg_info); + + const unsigned result = (unsigned)result_f; + if (result > 0) { + printf(BENCH_FORMAT"u\n", alg_info->name, result); + } else { + printf(BENCH_FORMAT"s\n", alg_info->name, "n/a"); + } + } + + dnssec_keystore_deinit(store); + + return KNOT_EOK; +} diff --git a/src/utils/keymgr/keystore.h b/src/utils/keymgr/keystore.h new file mode 100644 index 0000000..6adb5df --- /dev/null +++ b/src/utils/keymgr/keystore.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2024 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 <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "utils/keymgr/functions.h" + +int keymgr_keystore_test(const char *keystore_id, keymgr_list_params_t *params); + +int keymgr_keystore_bench(const char *keystore_id, keymgr_list_params_t *params, + uint16_t threads); diff --git a/src/utils/keymgr/main.c b/src/utils/keymgr/main.c index 355fd3a..b46aaa0 100644 --- a/src/utils/keymgr/main.c +++ b/src/utils/keymgr/main.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz> +/* Copyright (C) 2024 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 @@ -15,17 +15,20 @@ */ #include <getopt.h> +#include <signal.h> #include <stdlib.h> #include <unistd.h> #include "contrib/strtonum.h" #include "knot/dnssec/zone-keys.h" +#include "libdnssec/crypto.h" #include "libknot/libknot.h" #include "utils/common/msg.h" #include "utils/common/params.h" #include "utils/common/signal.h" #include "utils/common/util_conf.h" #include "utils/keymgr/functions.h" +#include "utils/keymgr/keystore.h" #include "utils/keymgr/offline_ksk.h" #define PROGRAM_NAME "keymgr" @@ -36,6 +39,7 @@ static void print_help(void) { printf("Usage:\n" " %s [-c | -C | -D <path>] [options] <zone_name> <command>\n" + " %s [-c | -C | -D <path>] [options] <keystore_id> <command>\n" " %s [-c | -C | -D <path>] [-j] -l\n" " %s -t <tsig_name> [<algorithm> [<bits>]]\n" "\n" @@ -87,6 +91,13 @@ static void print_help(void) " set Set existing key's timing attribute.\n" " (syntax: set <key_spec> <attribute_name>=<value>...)\n" "\n" + "Keystore commands:\n" + " keystore_test Conduct some tests on the specified keystore.\n" + " Use a configured keystore id or '-' for the default.\n" + " keystore_bench Conduct a signing benchmark for each supported algorithm.\n" + " Use a configured keystore id or '-' for the default.\n" + " (syntax: keystore_bench [<num_threads>])\n" + "\n" "Commands related to Offline KSK feature:\n" " pregenerate Pre-generate ZSKs for later rollovers with offline KSK.\n" " (syntax: pregenerate [<from>] <to>)\n" @@ -115,7 +126,8 @@ static void print_help(void) " ksk Whether the generated/imported key shall be Key Signing Key.\n" " created/publish/ready/active/retire/remove The timestamp of the key\n" " lifetime event (e.g. published=+1d active=1499770874)\n", - PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, CONF_DEFAULT_FILE, CONF_DEFAULT_DBDIR); + PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, CONF_DEFAULT_FILE, + CONF_DEFAULT_DBDIR); } static int key_command(int argc, char *argv[], int opt_ind, knot_lmdb_db_t *kaspdb, @@ -129,7 +141,8 @@ static int key_command(int argc, char *argv[], int opt_ind, knot_lmdb_db_t *kasp argc -= opt_ind; argv += opt_ind; - knot_dname_t *zone_name = knot_dname_from_str_alloc(argv[0]); + const char *id_str = argv[0]; + knot_dname_t *zone_name = knot_dname_from_str_alloc(id_str); if (zone_name == NULL) { return KNOT_ENOMEM; } @@ -275,6 +288,20 @@ static int key_command(int argc, char *argv[], int opt_ind, knot_lmdb_db_t *kasp } else if (strcmp(argv[1], "import-skr") == 0) { CHECK_MISSING_ARG("Input file not specified"); ret = keymgr_import_skr(&kctx, argv[2]); + } else if (strcmp(argv[1], "keystore-test") == 0) { + ret = keymgr_keystore_test(id_str, list_params); + print_ok_on_succes = false; + } else if (strcmp(argv[1], "keystore-bench") == 0) { + uint16_t threads = 1; + if (argc > 2) { + ret = str_to_u16(argv[2], &threads); + } + if (ret == KNOT_EOK && threads > 0) { + ret = keymgr_keystore_bench(id_str, list_params, threads); + } else { + ret = KNOT_EINVAL; + } + print_ok_on_succes = false; } else { ERR2("invalid command '%s'", argv[1]); goto main_end; @@ -317,8 +344,12 @@ int main(int argc, char *argv[]) tzset(); + dnssec_crypto_init(); + signal_ctx.close_db = &kaspdb; signal_init_std(); + struct sigaction sigact = { .sa_handler = SIG_IGN }; + sigaction(SIGALRM, &sigact, NULL); int ret; bool just_list = false; @@ -409,8 +440,10 @@ int main(int argc, char *argv[]) success: util_conf_deinit(); + dnssec_crypto_cleanup(); return EXIT_SUCCESS; failure: util_conf_deinit(); + dnssec_crypto_cleanup(); return EXIT_FAILURE; } diff --git a/src/utils/kzonecheck/main.c b/src/utils/kzonecheck/main.c index 5fb4c73..b602cc9 100644 --- a/src/utils/kzonecheck/main.c +++ b/src/utils/kzonecheck/main.c @@ -23,6 +23,7 @@ #include "libknot/libknot.h" #include "knot/common/log.h" #include "knot/zone/semantic-check.h" +#include "knot/zone/zone-load.h" #include "utils/common/msg.h" #include "utils/common/params.h" #include "utils/kzonecheck/zone_check.h" @@ -169,7 +170,8 @@ int main(int argc, char *argv[]) log_levels_add(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, LOG_UPTO(LOG_DEBUG)); } - int ret = zone_check(filename, zone, zonemd, optional, (time_t)check_time, print); + int ret = zone_check(filename, zone, zonemd, DEFAULT_TTL, optional, + (time_t)check_time, print); log_close(); if (ret == KNOT_EOK) { if (verbose && !print) { diff --git a/src/utils/kzonecheck/zone_check.c b/src/utils/kzonecheck/zone_check.c index 2ea63b8..46c8a8e 100644 --- a/src/utils/kzonecheck/zone_check.c +++ b/src/utils/kzonecheck/zone_check.c @@ -64,14 +64,14 @@ static void print_statistics(err_handler_stats_t *stats) } int zone_check(const char *zone_file, const knot_dname_t *zone_name, bool zonemd, - semcheck_optional_t optional, time_t time, bool print) + uint32_t dflt_ttl, semcheck_optional_t optional, time_t time, bool print) { err_handler_stats_t stats = { .handler = { .cb = err_callback }, }; zloader_t zl; - int ret = zonefile_open(&zl, zone_file, zone_name, optional, time); + int ret = zonefile_open(&zl, zone_file, zone_name, dflt_ttl, optional, time); switch (ret) { case KNOT_EOK: break; diff --git a/src/utils/kzonecheck/zone_check.h b/src/utils/kzonecheck/zone_check.h index 206c27e..6a7afdf 100644 --- a/src/utils/kzonecheck/zone_check.h +++ b/src/utils/kzonecheck/zone_check.h @@ -20,4 +20,4 @@ #include "libknot/libknot.h" int zone_check(const char *zone_file, const knot_dname_t *zone_name, bool zonemd, - semcheck_optional_t optional, time_t time, bool print); + uint32_t dflt_ttl, semcheck_optional_t optional, time_t time, bool print); |