Skip to content
Snippets Groups Projects
Commit a9044f62 authored by Jan Včelák's avatar Jan Včelák :rocket:
Browse files

Merge branch 'dnssec-check-key-validity' into 'master'

DNSSEC: constraint checks for KSK and ZSK

fixes #285

See merge request !256
parents e9ffa93b 7d3ac843
No related branches found
No related tags found
No related merge requests found
......@@ -271,8 +271,6 @@ The signing keys can be generated using ISC ``dnssec-keygen`` tool
only and there are some limitations:
* Keys for all zones must be placed in one directory.
* Algorithms based on RSA, DSA, and ECDSA are supported, support for
GOST algorithm is not finished yet.
* Only key publication, activation, inactivation, and removal time
stamps are utilized. Other time stamps are ignored.
* It is required, that both ``.private`` and ``.key`` files for each
......@@ -280,6 +278,7 @@ only and there are some limitations:
(even for verification only).
* There cannot be more than eight keys per zone. Keys which are not
published are not included in this number.
* Single-Type Signing Scheme is not supported.
Example how to generate NSEC3 capable zone signing key (ZSK) and key
signing key (KSK) for zone ``example.com``::
......
......@@ -106,6 +106,65 @@ static void set_zone_key_flags(const knot_key_params_t *params,
(params->time_delete == 0 || now < params->time_delete);
}
/*!
* \brief Check if there is a functional KSK and ZSK for each used algorithm.
*/
static int check_keys_validity(const knot_zone_keys_t *keys)
{
assert(keys);
const int MAX_ALGORITHMS = KNOT_DNSSEC_ALG_ECDSAP384SHA384 + 1;
struct {
bool published;
bool ksk_enabled;
bool zsk_enabled;
} algorithms[MAX_ALGORITHMS];
memset(algorithms, 0, sizeof(algorithms));
/* Make a list of used algorithms */
const knot_zone_key_t *key = NULL;
WALK_LIST(key, keys->list) {
knot_dnssec_algorithm_t a = key->dnssec_key.algorithm;
assert(a < MAX_ALGORITHMS);
if (key->is_public) {
// public key creates a requirement for an algorithm
algorithms[a].published = true;
// need fully enabled ZSK and KSK for each algorithm
if (key->is_active) {
if (key->is_ksk) {
algorithms[a].ksk_enabled = true;
} else {
algorithms[a].zsk_enabled = true;
}
}
}
}
/* Validate enabled algorithms */
int enabled_count = 0;
for (int a = 0; a < MAX_ALGORITHMS; a++) {
if (!algorithms[a].published) {
continue;
}
if (!algorithms[a].ksk_enabled || !algorithms[a].zsk_enabled) {
return KNOT_DNSSEC_EMISSINGKEYTYPE;
}
enabled_count += 1;
}
if (enabled_count == 0) {
return KNOT_DNSSEC_ENOKEY;
}
return KNOT_EOK;
}
/*!
* \brief Load zone keys from a key directory.
*
......@@ -230,8 +289,8 @@ int knot_load_zone_keys(const char *keydir_name, const knot_dname_t *zone_name,
closedir(keydir);
if (result == KNOT_EOK && EMPTY_LIST(keys->list)) {
result = KNOT_DNSSEC_ENOKEY;
if (result == KNOT_EOK) {
result = check_keys_validity(keys);
}
if (result == KNOT_EOK) {
......
......@@ -112,6 +112,7 @@ const error_table_t error_messages[] = {
{ KNOT_DNSSEC_ESIGN, "Cannot create the signature" },
{ KNOT_DNSSEC_ENOKEY, "No keys for signing" },
{ KNOT_DNSSEC_ENOKEYDIR, "Keydir does not exist" },
{ KNOT_DNSSEC_EMISSINGKEYTYPE, "Missing KSK or ZSK for used algorithm" },
/* NSEC3 errors. */
{ KNOT_NSEC3_ECOMPUTE_HASH, "Cannot compute NSEC3 hash" },
......
......@@ -128,6 +128,7 @@ enum knot_error {
KNOT_DNSSEC_ESIGN,
KNOT_DNSSEC_ENOKEY,
KNOT_DNSSEC_ENOKEYDIR,
KNOT_DNSSEC_EMISSINGKEYTYPE,
/* NSEC3 errors. */
KNOT_NSEC3_ECOMPUTE_HASH
......
#!/bin/sh
#
# Run this script every 50 years to refresh the keys. :-)
#
set -xe
TIME_PAST="-50y"
TIME_FUTURE="+50y"
keygen()
{
dnssec-keygen -r/dev/urandom $@
}
dir=$(pwd)
keydir=$(mktemp -d)
pushd "$keydir"
#
# valid scenarios
#
keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ok
keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_ok
keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_ok
keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ok
keygen -a ECDSAP256SHA256 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_ok
keygen -a ECDSAP256SHA256 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ok
keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_roll_ok
keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_roll_ok
keygen -a ECDSAP256SHA256 -P $TIME_FUTURE -A $TIME_PAST rsa_ecdsa_roll_ok
#
# invalid scenarios
#
keygen -a RSASHA256 -b 2048 -P $TIME_FUTURE -A $TIME_FUTURE -f KSK rsa_future_all
keygen -a RSASHA256 -b 1024 -P $TIME_FUTURE -A $TIME_FUTURE rsa_future_all
keygen -a RSASHA512 -b 2048 -P $TIME_FUTURE -A $TIME_PAST -f KSK rsa_future_publish
keygen -a RSASHA256 -b 1024 -P $TIME_FUTURE -A $TIME_PAST rsa_future_publish
keygen -a RSASHA512 -b 2048 -P $TIME_PAST -A $TIME_FUTURE -f KSK rsa_future_active
keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_FUTURE rsa_future_active
keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_inactive_zsk
keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_FUTURE rsa_inactive_zsk
keygen -a RSASHA256 -b 2048 -P $TIME_FUTURE -A $TIME_FUTURE -f KSK rsa_no_zsk
keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_no_zsk
keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_twice_ksk
keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_twice_ksk
keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ksk_only
keygen -a RSASHA256 -b 1024 -P $TIME_PAST -A $TIME_PAST rsa_ecdsa_ksk_only
keygen -a ECDSAP256SHA256 -P $TIME_PAST -A $TIME_PAST -f KSK rsa_ecdsa_ksk_only
keygen -a RSASHA256 -b 2048 -P $TIME_PAST -A $TIME_PAST -f KSK rsa256_rsa512
keygen -a RSASHA512 -b 2048 -P $TIME_PAST -A $TIME_PAST rsa256_rsa512
tar czf "$dir/keys.tgz" K*.{key,private}
popd
rm -rf "$keydir"
File added
#!/usr/bin/env python3
"""
Validate ZSK and KSK constrains checks.
"""
import tarfile
import os.path
import dnstest.zonefile
from dnstest.test import Test
TEST_CASES = {
# valid cases
"rsa_ok": True,
"rsa_ecdsa_ok": True,
"rsa_ecdsa_roll_ok": True,
# invalid cases
"rsa_future_all": False,
"rsa_future_publish": False,
"rsa_future_active": False,
"rsa_inactive_zsk": False,
"rsa_no_zsk": False,
"rsa_twice_ksk": False,
"rsa_ecdsa_ksk_only": False,
"rsa256_rsa512": False,
}
t = Test()
knot = t.server("knot")
knot.dnssec_enable = True
# setup keys
keys_archive = os.path.join(t.data_dir, "keys.tgz")
with tarfile.open(keys_archive, "r:*") as tar:
tar.extractall(knot.keydir)
# setup zones
zones = []
for zone_name in TEST_CASES:
zone = dnstest.zonefile.ZoneFile(t.zones_dir)
zone.set_name(zone_name)
zone.gen_file(dnssec=False, nsec3=False, records=5)
zones.append(zone)
t.link(zones, knot)
t.start()
for zone, valid in TEST_CASES.items():
expected_rcode = "NOERROR" if valid else "SERVFAIL"
knot.dig(zone, "SOA").check(rcode=expected_rcode)
t.end()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment