Commit d44d2b82 authored by Libor Peltan's avatar Libor Peltan Committed by Daniel Salzman

dnssec: refactored KSK flag; refactored zone-keys CSK handling, add CSK flag to KASP

parent 43389b1b
...@@ -233,6 +233,7 @@ static int serialize_key_params(const key_params_t *params, const knot_dname_t * ...@@ -233,6 +233,7 @@ static int serialize_key_params(const key_params_t *params, const knot_dname_t *
uint8_t flags = 0x02; uint8_t flags = 0x02;
flags |= (params->is_ksk ? 0x01 : 0); flags |= (params->is_ksk ? 0x01 : 0);
flags |= (params->is_pub_only ? 0x04 : 0); flags |= (params->is_pub_only ? 0x04 : 0);
flags |= (params->is_csk ? 0x08 : 0);
wire_ctx_write_u8(&wire, flags); wire_ctx_write_u8(&wire, flags);
wire_ctx_write_u64(&wire, (uint64_t)params->timing.created); wire_ctx_write_u64(&wire, (uint64_t)params->timing.created);
wire_ctx_write_u64(&wire, (uint64_t)params->timing.pre_active); wire_ctx_write_u64(&wire, (uint64_t)params->timing.pre_active);
...@@ -274,6 +275,10 @@ static int deserialize_key_params(key_params_t *params, const knot_db_val_t *key ...@@ -274,6 +275,10 @@ static int deserialize_key_params(key_params_t *params, const knot_db_val_t *key
uint8_t isksk_plus_flags = wire_ctx_read_u8(&wire); uint8_t isksk_plus_flags = wire_ctx_read_u8(&wire);
params->is_ksk = ((isksk_plus_flags & (uint8_t)0x01) != (uint8_t)0x00); params->is_ksk = ((isksk_plus_flags & (uint8_t)0x01) != (uint8_t)0x00);
params->is_pub_only = ((isksk_plus_flags & (uint8_t)0x04) != (uint8_t)0x00); params->is_pub_only = ((isksk_plus_flags & (uint8_t)0x04) != (uint8_t)0x00);
params->is_csk = ((isksk_plus_flags & (uint8_t)0x08) != (uint8_t)0x00);
if (params->is_csk && !params->is_ksk) {
return KNOT_EMALF;
}
if ((isksk_plus_flags & (uint8_t)0x02) != (uint8_t)0x00) { if ((isksk_plus_flags & (uint8_t)0x02) != (uint8_t)0x00) {
params->timing.created = (knot_time_t)wire_ctx_read_u64(&wire); params->timing.created = (knot_time_t)wire_ctx_read_u64(&wire);
params->timing.pre_active = (knot_time_t)wire_ctx_read_u64(&wire); params->timing.pre_active = (knot_time_t)wire_ctx_read_u64(&wire);
......
...@@ -98,6 +98,9 @@ static int params2kaspkey(const knot_dname_t *dname, key_params_t *params, ...@@ -98,6 +98,9 @@ static int params2kaspkey(const knot_dname_t *dname, key_params_t *params,
key->timing = params->timing; key->timing = params->timing;
key->is_pub_only = params->is_pub_only; key->is_pub_only = params->is_pub_only;
assert(params->is_ksk || !params->is_csk);
key->is_ksk = params->is_ksk;
key->is_zsk = (params->is_csk || !params->is_ksk);
return KNOT_EOK; return KNOT_EOK;
} }
...@@ -111,6 +114,8 @@ static void kaspkey2params(knot_kasp_key_t *key, key_params_t *params) ...@@ -111,6 +114,8 @@ static void kaspkey2params(knot_kasp_key_t *key, key_params_t *params)
dnssec_key_get_pubkey(key->key, &params->public_key); dnssec_key_get_pubkey(key->key, &params->public_key);
params->algorithm = dnssec_key_get_algorithm(key->key); params->algorithm = dnssec_key_get_algorithm(key->key);
params->is_ksk = dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK; params->is_ksk = dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK;
assert(params->is_ksk == key->is_ksk);
params->is_csk = (key->is_ksk && key->is_zsk);
params->timing = key->timing; params->timing = key->timing;
params->is_pub_only = key->is_pub_only; params->is_pub_only = key->is_pub_only;
} }
......
...@@ -42,6 +42,7 @@ typedef struct { ...@@ -42,6 +42,7 @@ typedef struct {
typedef struct { typedef struct {
char *id; char *id;
bool is_ksk; bool is_ksk;
bool is_csk;
bool is_pub_only; bool is_pub_only;
uint16_t keytag; uint16_t keytag;
uint8_t algorithm; uint8_t algorithm;
...@@ -57,6 +58,8 @@ typedef struct { ...@@ -57,6 +58,8 @@ typedef struct {
dnssec_key_t *key; /*!< Instance of the key. */ dnssec_key_t *key; /*!< Instance of the key. */
knot_kasp_key_timing_t timing; /*!< Key timing information. */ knot_kasp_key_timing_t timing; /*!< Key timing information. */
bool is_pub_only; bool is_pub_only;
bool is_ksk;
bool is_zsk;
} knot_kasp_key_t; } knot_kasp_key_t;
/*! /*!
......
...@@ -78,12 +78,12 @@ static knot_kasp_key_t *key_get_by_id(kdnssec_ctx_t *ctx, const char *keyid) ...@@ -78,12 +78,12 @@ static knot_kasp_key_t *key_get_by_id(kdnssec_ctx_t *ctx, const char *keyid)
return NULL; return NULL;
} }
static int generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_time_t when_active, bool pre_active) static int generate_key(kdnssec_ctx_t *ctx, bool ksk, bool zsk, knot_time_t when_active, bool pre_active)
{ {
assert(!pre_active || when_active == 0); assert(!pre_active || when_active == 0);
knot_kasp_key_t *key = NULL; knot_kasp_key_t *key = NULL;
int ret = kdnssec_generate_key(ctx, ksk, &key); int ret = kdnssec_generate_key(ctx, ksk, zsk, &key);
if (ret != KNOT_EOK) { if (ret != KNOT_EOK) {
return ret; return ret;
} }
...@@ -98,7 +98,7 @@ static int generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_time_t when_active, b ...@@ -98,7 +98,7 @@ static int generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_time_t when_active, b
return KNOT_EOK; return KNOT_EOK;
} }
static int share_or_generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_time_t when_active, bool pre_active) static int share_or_generate_key(kdnssec_ctx_t *ctx, bool ksk, bool zsk, knot_time_t when_active, bool pre_active)
{ {
assert(!pre_active || when_active == 0); assert(!pre_active || when_active == 0);
...@@ -118,7 +118,7 @@ static int share_or_generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_time_t when_ ...@@ -118,7 +118,7 @@ static int share_or_generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_time_t when_
// if we already have the policy-last key, we have to generate new one // if we already have the policy-last key, we have to generate new one
if (ret == KNOT_ENOENT || key_id_present(ctx, borrow_key, DNSKEY_FLAGS_KSK)) { if (ret == KNOT_ENOENT || key_id_present(ctx, borrow_key, DNSKEY_FLAGS_KSK)) {
knot_kasp_key_t *key = NULL; knot_kasp_key_t *key = NULL;
ret = kdnssec_generate_key(ctx, ksk, &key); ret = kdnssec_generate_key(ctx, ksk, zsk, &key);
if (ret != KNOT_EOK) { if (ret != KNOT_EOK) {
return ret; return ret;
} }
...@@ -180,7 +180,6 @@ static bool running_rollover(const kdnssec_ctx_t *ctx) ...@@ -180,7 +180,6 @@ static bool running_rollover(const kdnssec_ctx_t *ctx)
for (size_t i = 0; i < ctx->zone->num_keys; i++) { for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i]; knot_kasp_key_t *key = &ctx->zone->keys[i];
bool isksk = (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK);
switch (get_key_state(key, ctx->now)) { switch (get_key_state(key, ctx->now)) {
case DNSSEC_KEY_STATE_PRE_ACTIVE: case DNSSEC_KEY_STATE_PRE_ACTIVE:
res = true; res = true;
...@@ -189,10 +188,10 @@ static bool running_rollover(const kdnssec_ctx_t *ctx) ...@@ -189,10 +188,10 @@ static bool running_rollover(const kdnssec_ctx_t *ctx)
res = (res || !key->is_pub_only); res = (res || !key->is_pub_only);
break; break;
case DNSSEC_KEY_STATE_READY: case DNSSEC_KEY_STATE_READY:
ready_ksk = (ready_ksk || isksk); ready_ksk = (ready_ksk || key->is_ksk);
break; break;
case DNSSEC_KEY_STATE_ACTIVE: case DNSSEC_KEY_STATE_ACTIVE:
active_ksk = (active_ksk || isksk); active_ksk = (active_ksk || key->is_ksk);
break; break;
case DNSSEC_KEY_STATE_RETIRE_ACTIVE: case DNSSEC_KEY_STATE_RETIRE_ACTIVE:
case DNSSEC_KEY_STATE_POST_ACTIVE: case DNSSEC_KEY_STATE_POST_ACTIVE:
...@@ -337,8 +336,7 @@ static roll_action_t next_action(kdnssec_ctx_t *ctx) ...@@ -337,8 +336,7 @@ static roll_action_t next_action(kdnssec_ctx_t *ctx)
if (key->is_pub_only) { if (key->is_pub_only) {
continue; continue;
} }
bool isksk = (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK); if (key->is_ksk) {
if (isksk) {
switch (get_key_state(key, ctx->now)) { switch (get_key_state(key, ctx->now)) {
case DNSSEC_KEY_STATE_PRE_ACTIVE: case DNSSEC_KEY_STATE_PRE_ACTIVE:
keytime = alg_publish_time(key->timing.pre_active, ctx); keytime = alg_publish_time(key->timing.pre_active, ctx);
...@@ -419,7 +417,7 @@ static roll_action_t next_action(kdnssec_ctx_t *ctx) ...@@ -419,7 +417,7 @@ static roll_action_t next_action(kdnssec_ctx_t *ctx)
} }
if (knot_time_cmp(keytime, res.time) < 0) { if (knot_time_cmp(keytime, res.time) < 0) {
res.key = key; res.key = key;
res.ksk = isksk; res.ksk = key->is_ksk;
res.time = keytime; res.time = keytime;
res.type = restype; res.type = restype;
} }
...@@ -431,13 +429,12 @@ static roll_action_t next_action(kdnssec_ctx_t *ctx) ...@@ -431,13 +429,12 @@ static roll_action_t next_action(kdnssec_ctx_t *ctx)
static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey) static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
{ {
assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED); assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
assert(dnssec_key_get_flags(newkey->key) == DNSKEY_FLAGS_KSK); assert(newkey->is_ksk);
// pushing from READY into ACTIVE decreases the other key's cds_priority // pushing from READY into ACTIVE decreases the other key's cds_priority
for (size_t i = 0; i < ctx->zone->num_keys; i++) { for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i]; knot_kasp_key_t *key = &ctx->zone->keys[i];
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK && if (key->is_ksk && get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
key->timing.active = ctx->now; key->timing.active = ctx->now;
} }
} }
...@@ -448,20 +445,17 @@ static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey) ...@@ -448,20 +445,17 @@ static int submit_key(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey) static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
{ {
uint16_t kskflag = dnssec_key_get_flags(newkey->key); if (newkey->is_ksk) {
if (kskflag == DNSKEY_FLAGS_KSK) {
log_zone_notice(ctx->zone->dname, "DNSSEC, KSK submission, confirmed"); log_zone_notice(ctx->zone->dname, "DNSSEC, KSK submission, confirmed");
} }
for (size_t i = 0; i < ctx->zone->num_keys; i++) { for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i]; knot_kasp_key_t *key = &ctx->zone->keys[i];
uint16_t keyflags = dnssec_key_get_flags(key->key);
key_state_t keystate = get_key_state(key, ctx->now); key_state_t keystate = get_key_state(key, ctx->now);
uint8_t keyalg = dnssec_key_get_algorithm(key->key); uint8_t keyalg = dnssec_key_get_algorithm(key->key);
if (keyflags == kskflag && keystate == DNSSEC_KEY_STATE_ACTIVE) { if (((newkey->is_ksk && key->is_ksk) || (newkey->is_zsk && key->is_zsk))
if (keyflags == DNSKEY_FLAGS_KSK || && keystate == DNSSEC_KEY_STATE_ACTIVE) {
keyalg != dnssec_key_get_algorithm(newkey->key)) { if (key->is_ksk || keyalg != dnssec_key_get_algorithm(newkey->key)) {
key->timing.retire_active = ctx->now; key->timing.retire_active = ctx->now;
} else { } else {
key->timing.retire = ctx->now; key->timing.retire = ctx->now;
...@@ -469,7 +463,7 @@ static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey) ...@@ -469,7 +463,7 @@ static int exec_new_signatures(kdnssec_ctx_t *ctx, knot_kasp_key_t *newkey)
} }
} }
if (kskflag == DNSKEY_FLAGS_KSK) { if (newkey->is_ksk) {
assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_READY); assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_READY);
} else { } else {
assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED); assert(get_key_state(newkey, ctx->now) == DNSSEC_KEY_STATE_PUBLISHED);
...@@ -494,8 +488,7 @@ static int exec_ksk_retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *key) ...@@ -494,8 +488,7 @@ static int exec_ksk_retire(kdnssec_ctx_t *ctx, knot_kasp_key_t *key)
for (size_t i = 0; i < ctx->zone->num_keys; i++) { for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i]; knot_kasp_key_t *key = &ctx->zone->keys[i];
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_ZSK && if (key->is_zsk && get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE) {
get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_RETIRE_ACTIVE) {
alg_rollover = true; alg_rollover = true;
alg_rollover_friend = key; alg_rollover_friend = key;
} }
...@@ -535,9 +528,9 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *resched ...@@ -535,9 +528,9 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *resched
// generate initial keys if missing // generate initial keys if missing
if (!key_present(ctx, DNSKEY_FLAGS_KSK)) { if (!key_present(ctx, DNSKEY_FLAGS_KSK)) {
if (ctx->policy->ksk_shared) { if (ctx->policy->ksk_shared) {
ret = share_or_generate_key(ctx, true, ctx->now, false); ret = share_or_generate_key(ctx, true, ctx->policy->singe_type_signing, ctx->now, false);
} else { } else {
ret = generate_key(ctx, true, ctx->now, false); ret = generate_key(ctx, true, ctx->policy->singe_type_signing, ctx->now, false);
} }
reschedule->plan_ds_query = true; reschedule->plan_ds_query = true;
if (ret == KNOT_EOK) { if (ret == KNOT_EOK) {
...@@ -546,7 +539,7 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *resched ...@@ -546,7 +539,7 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *resched
} }
if (!ctx->policy->singe_type_signing && ret == KNOT_EOK && if (!ctx->policy->singe_type_signing && ret == KNOT_EOK &&
!key_present(ctx, DNSKEY_FLAGS_ZSK)) { !key_present(ctx, DNSKEY_FLAGS_ZSK)) {
ret = generate_key(ctx, false, ctx->now, false); ret = generate_key(ctx, false, true, ctx->now, false);
if (ret == KNOT_EOK) { if (ret == KNOT_EOK) {
reschedule->keys_changed = true; reschedule->keys_changed = true;
} }
...@@ -555,12 +548,12 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *resched ...@@ -555,12 +548,12 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *resched
if (!algorithm_present(ctx,ctx->policy->algorithm) && if (!algorithm_present(ctx,ctx->policy->algorithm) &&
!running_rollover(ctx) && ret == KNOT_EOK) { !running_rollover(ctx) && ret == KNOT_EOK) {
if (ctx->policy->ksk_shared) { if (ctx->policy->ksk_shared) {
ret = share_or_generate_key(ctx, true, 0, true); ret = share_or_generate_key(ctx, true, ctx->policy->singe_type_signing, 0, true);
} else { } else {
ret = generate_key(ctx, true, 0, true); ret = generate_key(ctx, true, ctx->policy->singe_type_signing, 0, true);
} }
if (!ctx->policy->singe_type_signing && ret == KNOT_EOK) { if (!ctx->policy->singe_type_signing && ret == KNOT_EOK) {
ret = generate_key(ctx, false, 0, true); ret = generate_key(ctx, false, true, 0, true);
} }
log_zone_info(ctx->zone->dname, "DNSSEC, algorithm rollover started"); log_zone_info(ctx->zone->dname, "DNSSEC, algorithm rollover started");
if (ret == KNOT_EOK) { if (ret == KNOT_EOK) {
...@@ -579,9 +572,9 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *resched ...@@ -579,9 +572,9 @@ int knot_dnssec_key_rollover(kdnssec_ctx_t *ctx, zone_sign_reschedule_t *resched
switch (next.type) { switch (next.type) {
case GENERATE: case GENERATE:
if (next.ksk && ctx->policy->ksk_shared) { if (next.ksk && ctx->policy->ksk_shared) {
ret = share_or_generate_key(ctx, next.ksk, 0, false); ret = share_or_generate_key(ctx, true, ctx->policy->singe_type_signing, 0, false);
} else { } else {
ret = generate_key(ctx, next.ksk, 0, false); ret = generate_key(ctx, next.ksk, ctx->policy->singe_type_signing || !next.ksk, 0, false);
} }
if (ret == KNOT_EOK) { if (ret == KNOT_EOK) {
log_zone_info(ctx->zone->dname, "DNSSEC, %cSK rollover started", log_zone_info(ctx->zone->dname, "DNSSEC, %cSK rollover started",
...@@ -634,8 +627,7 @@ int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx) ...@@ -634,8 +627,7 @@ int knot_dnssec_ksk_sbm_confirm(kdnssec_ctx_t *ctx)
{ {
for (size_t i = 0; i < ctx->zone->num_keys; i++) { for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i]; knot_kasp_key_t *key = &ctx->zone->keys[i];
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK && if (key->is_ksk && get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY) {
int ret = exec_new_signatures(ctx, key); int ret = exec_new_signatures(ctx, key);
if (ret == KNOT_EOK) { if (ret == KNOT_EOK) {
ret = kdnssec_ctx_commit(ctx); ret = kdnssec_ctx_commit(ctx);
...@@ -652,7 +644,7 @@ bool zone_has_key_sbm(const kdnssec_ctx_t *ctx) ...@@ -652,7 +644,7 @@ bool zone_has_key_sbm(const kdnssec_ctx_t *ctx)
for (size_t i = 0; i < ctx->zone->num_keys; i++) { for (size_t i = 0; i < ctx->zone->num_keys; i++) {
knot_kasp_key_t *key = &ctx->zone->keys[i]; knot_kasp_key_t *key = &ctx->zone->keys[i];
if (dnssec_key_get_flags(key->key) == DNSKEY_FLAGS_KSK && if (key->is_ksk &&
(get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY || (get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_READY ||
get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_ACTIVE)) { get_key_state(key, ctx->now) == DNSSEC_KEY_STATE_ACTIVE)) {
return true; return true;
......
...@@ -161,8 +161,7 @@ int knot_dnssec_zone_sign(zone_update_t *update, ...@@ -161,8 +161,7 @@ int knot_dnssec_zone_sign(zone_update_t *update,
goto done; goto done;
} }
result = load_zone_keys(ctx.zone, ctx.keystore, result = load_zone_keys(&ctx, &keyset, true);
ctx.policy->nsec3_enabled, ctx.now, &keyset, true);
if (result != KNOT_EOK) { if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to load keys (%s)", log_zone_error(zone_name, "DNSSEC, failed to load keys (%s)",
knot_strerror(result)); knot_strerror(result));
...@@ -247,8 +246,7 @@ int knot_dnssec_sign_update(zone_update_t *update, zone_sign_reschedule_t *resch ...@@ -247,8 +246,7 @@ int knot_dnssec_sign_update(zone_update_t *update, zone_sign_reschedule_t *resch
goto done; goto done;
} }
result = load_zone_keys(ctx.zone, ctx.keystore, result = load_zone_keys(&ctx, &keyset, false);
ctx.policy->nsec3_enabled, ctx.now, &keyset, false);
if (result != KNOT_EOK) { if (result != KNOT_EOK) {
log_zone_error(zone_name, "DNSSEC, failed to load keys (%s)", log_zone_error(zone_name, "DNSSEC, failed to load keys (%s)",
knot_strerror(result)); knot_strerror(result));
......
...@@ -34,12 +34,13 @@ uint16_t dnskey_flags(bool is_ksk) ...@@ -34,12 +34,13 @@ uint16_t dnskey_flags(bool is_ksk)
return is_ksk ? DNSKEY_FLAGS_KSK : DNSKEY_FLAGS_ZSK; return is_ksk ? DNSKEY_FLAGS_KSK : DNSKEY_FLAGS_ZSK;
} }
int kdnssec_generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_kasp_key_t **key_ptr) int kdnssec_generate_key(kdnssec_ctx_t *ctx, bool ksk, bool zsk, knot_kasp_key_t **key_ptr)
{ {
assert(ctx); assert(ctx);
assert(ctx->zone); assert(ctx->zone);
assert(ctx->keystore); assert(ctx->keystore);
assert(ctx->policy); assert(ctx->policy);
assert(ksk || zsk);
dnssec_key_algorithm_t algorithm = ctx->policy->algorithm; dnssec_key_algorithm_t algorithm = ctx->policy->algorithm;
unsigned size = ksk ? ctx->policy->ksk_size : ctx->policy->zsk_size; unsigned size = ksk ? ctx->policy->ksk_size : ctx->policy->zsk_size;
...@@ -87,6 +88,8 @@ int kdnssec_generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_kasp_key_t **key_ptr ...@@ -87,6 +88,8 @@ int kdnssec_generate_key(kdnssec_ctx_t *ctx, bool ksk, knot_kasp_key_t **key_ptr
key->id = id; key->id = id;
key->key = dnskey; key->key = dnskey;
key->is_ksk = ksk;
key->is_zsk = zsk;
key->timing.created = ctx->now; key->timing.created = ctx->now;
r = kasp_zone_append(ctx->zone, key); r = kasp_zone_append(ctx->zone, key);
...@@ -207,11 +210,8 @@ static int set_key(knot_kasp_key_t *kasp_key, knot_time_t now, zone_key_t *zone_ ...@@ -207,11 +210,8 @@ static int set_key(knot_kasp_key_t *kasp_key, knot_time_t now, zone_key_t *zone_
zone_key->next_event = next; zone_key->next_event = next;
// key use flags zone_key->is_ksk = kasp_key->is_ksk;
zone_key->is_zsk = kasp_key->is_zsk;
uint16_t flags = dnssec_key_get_flags(kasp_key->key);
zone_key->is_ksk = flags & KNOT_RDATA_DNSKEY_FLAG_KSK;
zone_key->is_zsk = !zone_key->is_ksk;
zone_key->is_public = (knot_time_cmp(timing->publish, now) <= 0 && zone_key->is_public = (knot_time_cmp(timing->publish, now) <= 0 &&
knot_time_cmp(timing->post_active, now) > 0 && knot_time_cmp(timing->post_active, now) > 0 &&
...@@ -246,112 +246,77 @@ static bool is_nsec3_allowed(uint8_t algorithm) ...@@ -246,112 +246,77 @@ static bool is_nsec3_allowed(uint8_t algorithm)
} }
} }
/*! static void ksk2csk(kdnssec_ctx_t *ctx, zone_keyset_t *keyset, uint8_t alg)
* \brief Algorithm usage information.
*/
typedef struct algorithm_usage {
unsigned ksk_count; //!< Available KSK count.
unsigned zsk_count; //!< Available ZSK count.
bool is_public; //!< DNSKEY is published.
bool is_stss; //!< Used to sign all types of records.
bool is_ksk_active; //!< Used to sign DNSKEY records.
bool is_zsk_active; //!< Used to sign non-DNSKEY records.
} algorithm_usage_t;
/*!
* \brief Check correct key usage, enable Single-Type Signing Scheme if needed.
*
* Each record in the zone has to be signed at least by one key for each
* algorithm published in the DNSKEY RR set in the zone apex.
*
* Therefore, publishing a DNSKEY creates a requirement on active keys with
* the same algorithm. At least one KSK key and one ZSK has to be enabled.
* If one key type is unavailable (not just inactive and not-published), the
* algorithm is switched to Single-Type Signing Scheme.
*/
static int prepare_and_check_keys(const knot_dname_t *zone_name, bool nsec3_enabled,
zone_keyset_t *keyset)
{ {
assert(zone_name); for (size_t j = 0; j < keyset->count; j++) {
assert(keyset); zone_key_t *key = &keyset->keys[j];
if (dnssec_key_get_algorithm(key->key) == alg) {
assert(key->is_ksk);
key->is_zsk = true;
}
}
const size_t max_algorithms = KNOT_DNSSEC_ALG_ED25519 + 1; for (size_t i = 0; i < ctx->zone->num_keys; i++) {
algorithm_usage_t usage[max_algorithms]; knot_kasp_key_t *key = &ctx->zone->keys[i];
memset(usage, 0, max_algorithms * sizeof(algorithm_usage_t)); if (dnssec_key_get_algorithm(key->key) == alg) {
assert(key->is_ksk);
key->is_zsk = true;
}
}
// count available keys log_zone_info(ctx->zone->dname, "DNSSEC, Single-Type Signing "
"Scheme enabled");
}
static int walk_algorithms(kdnssec_ctx_t *ctx, zone_keyset_t *keyset)
{
uint8_t alg_usage[256] = { 0 };
bool keys_changed = false, have_active_alg = false;
for (size_t i = 0; i < keyset->count; i++) { for (size_t i = 0; i < keyset->count; i++) {
zone_key_t *key = &keyset->keys[i]; zone_key_t *key = &keyset->keys[i];
uint8_t algorithm = dnssec_key_get_algorithm(key->key); uint8_t alg = dnssec_key_get_algorithm(key->key);
assert(algorithm < max_algorithms); if (ctx->policy->nsec3_enabled && !is_nsec3_allowed(alg)) {
algorithm_usage_t *u = &usage[algorithm]; log_zone_warning(ctx->zone->dname, "DNSSEC, key %d "
"cannot be used with NSEC3",
if (nsec3_enabled && !is_nsec3_allowed(algorithm)) { dnssec_key_get_keytag(key->key));
log_zone_warning(zone_name, "DNSSEC, key %d "
"cannot be used with NSEC3",
dnssec_key_get_keytag(key->key));
key->is_public = false; key->is_public = false;
key->is_active = false; key->is_active = false;
key->cds_priority = 0; key->cds_priority = 0;
continue; continue;
} }
if (key->is_ksk) { u->ksk_count += 1; } if (key->is_ksk && key->is_public) { alg_usage[alg] |= 1; }
if (key->is_zsk) { u->zsk_count += 1; } if (key->is_zsk && key->is_public) { alg_usage[alg] |= 2; }
if (key->is_ksk && key->is_active) { alg_usage[alg] |= 4; }
if (key->is_zsk && key->is_active) { alg_usage[alg] |= 8; }
} }
// enable Single-Type Signing scheme if applicable for (size_t i = 0; i < sizeof(alg_usage); i++) {
if (!(alg_usage[i] & 3)) {
for (int i = 0; i < max_algorithms; i++) { continue; // no public keys, ignore
algorithm_usage_t *u = &usage[i];
// either KSK or ZSK keys are available
if ((u->ksk_count == 0) != (u->zsk_count == 0)) {
u->is_stss = true;
log_zone_info(zone_name, "DNSSEC, Single-Type Signing "
"scheme enabled, algorithm %d", i);
} }
} switch (alg_usage[i]) {
case 5: // because migrating from older version OR from manual setup
// update key flags for STSS, collect information about usage ksk2csk(ctx, keyset, i);
alg_usage[i] |= 10;
for (size_t i = 0; i < keyset->count; i++) { keys_changed = true;
zone_key_t *key = &keyset->keys[i]; // FALLTHROUGH
algorithm_usage_t *u = &usage[dnssec_key_get_algorithm(key->key)]; case 15: // all keys ready for signing
have_active_alg = true;
if (u->is_stss) { break;
key->is_ksk = true; default:
key->is_zsk = true; return KNOT_DNSSEC_EMISSINGKEYTYPE;
}
if (key->is_public) { u->is_public = true; }
if (key->is_ksk && key->is_active) {
u->is_ksk_active = true;
}
if (key->is_zsk && key->is_active) {
u->is_zsk_active = true;
} }
} }
// validate conditions for used algorithms if (!have_active_alg) {
return KNOT_DNSSEC_ENOKEY;
unsigned public_count = 0;
for (int i = 0; i < max_algorithms; i++) {
algorithm_usage_t *u = &usage[i];
if (u->is_public) {
public_count += 1;
if (!u->is_ksk_active || !u->is_zsk_active) {
return KNOT_DNSSEC_EMISSINGKEYTYPE;
}
}
} }
if (public_count == 0) { if (keys_changed) {
return KNOT_DNSSEC_ENOKEY; return kdnssec_ctx_commit(ctx);
} }
return KNOT_EOK; return KNOT_EOK;
...@@ -398,11 +363,11 @@ static void log_key_info(const zone_key_t *key, char *out, size_t out_len) ...@@ -398,11 +363,11 @@ static void log_key_info(const zone_key_t *key, char *out, size_t out_len)
(void)snprintf(out, out_len, "DNSSEC, key, tag %5d, algorithm %s%s%s%s%s", (void)snprintf(out, out_len, "DNSSEC, key, tag %5d, algorithm %s%s%s%s%s",
dnssec_key_get_keytag(key->key), dnssec_key_get_keytag(key->key),
(alg != NULL ? alg->name : alg_code_str), (alg != NULL ? alg->name : alg_code_str),
(key->is_ksk ? ", KSK" : ""), (key->is_ksk ? (key->is_zsk ? ", CSK" : ", KSK") : ""),
(key->is_public ? ", public" : ""), (key->is_public ? ", public" : ""),
(key->cds_priority > 1 ? ", ready" : ""), (key->cds_priority > 1 ? ", ready" : ""),
(key->is_active ? ", active" : "")); (key->is_active ? ", active" : ""));
} }
int log_key_sort(const void *a, const void *b) int log_key_sort(const void *a, const void *b)
...@@ -417,32 +382,30 @@ int log_key_sort(const void *a, const void *b) ...@@ -417,32 +382,30 @@ int log_key_sort(const void *a, const void *b)
/*! /*!
* \brief Load zone keys and init cryptographic context. * \brief Load zone keys and init cryptographic context.
*/ */
int load_zone_keys(knot_kasp_zone_t *zone, dnssec_keystore_t *store, int load_zone_keys(kdnssec_ctx_t *ctx, zone_keyset_t *keyset_ptr, bool verbose)
bool nsec3_enabled, knot_time_t now, zone_keyset_t *keyset_ptr,
bool verbose)
{ {
if (!zone || !store || !keyset_ptr) { if (!ctx || !keyset_ptr) {
return KNOT_EINVAL; return KNOT_EINVAL;
} }
zone_keyset_t keyset = { 0 }; zone_keyset_t keyset = { 0 };
if (zone->num_keys < 1) { if (ctx->zone->num_keys < 1) {
log_zone_error(zone->dname, "DNSSEC, no keys are available"); log_zone_error(ctx->zone->dname, "DNSSEC, no keys are available");
return KNOT_DNSSEC_ENOKEY; return KNOT_DNSSEC_ENOKEY;
} }
keyset.count = zone->num_keys; keyset.count = ctx->zone->num_keys;
keyset.keys = calloc(keyset.count, sizeof(zone_key_t)); keyset.keys = calloc(keyset.count, sizeof(zone_key_t));
if (!keyset.keys) { if (!keyset.keys) {
free_zone_keys(&keyset); free_zone_keys(&keyset);
return KNOT_ENOMEM; return KNOT_ENOMEM;
} }