2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 14:35:26 +00:00

Merge branch 'matthijs-offline-ksk-add-ksk-on-sign' into 'main'

Add DNSKEY record for KSK when creating the SKR

See merge request isc-projects/bind9!8986
This commit is contained in:
Matthijs Mekking
2024-05-06 09:30:10 +00:00
3 changed files with 77 additions and 54 deletions

View File

@@ -87,7 +87,6 @@ static int min_dh = 128;
#define KSR_LINESIZE 1500 /* should be long enough for any DNSKEY record */
#define DATETIME_INDEX 25
#define TTL_MAX INT32_MAX
#define MAXWIRE (64 * 1024)
#define STR(t) ((t).value.as_textregion.base)
@@ -523,10 +522,7 @@ print_rdata(dns_rdataset_t *rrset) {
static isc_stdtime_t
print_dnskeys(dns_kasp_key_t *kaspkey, dns_ttl_t ttl, dns_dnsseckeylist_t *keys,
isc_stdtime_t inception, isc_stdtime_t next_inception) {
bool ksk = dns_kasp_key_ksk(kaspkey);
bool zsk = dns_kasp_key_zsk(kaspkey);
char algstr[DNS_SECALG_FORMATSIZE];
char rolestr[4];
char timestr[26]; /* Minimal buf as per ctime_r() spec. */
dns_rdatalist_t *rdatalist = NULL;
dns_rdataset_t rdataset = DNS_RDATASET_INIT;
@@ -536,13 +532,6 @@ print_dnskeys(dns_kasp_key_t *kaspkey, dns_ttl_t ttl, dns_dnsseckeylist_t *keys,
isc_stdtime_tostring(inception, timestr, sizeof(timestr));
dns_secalg_format(dns_kasp_key_algorithm(kaspkey), algstr,
sizeof(algstr));
if (ksk && zsk) {
snprintf(rolestr, sizeof(rolestr), "csk");
} else if (ksk) {
snprintf(rolestr, sizeof(rolestr), "ksk");
} else {
snprintf(rolestr, sizeof(rolestr), "zsk");
}
/* Fetch matching key pair. */
rdatalist = isc_mem_get(mctx, sizeof(*rdatalist));
@@ -598,8 +587,8 @@ print_dnskeys(dns_kasp_key_t *kaspkey, dns_ttl_t ttl, dns_dnsseckeylist_t *keys,
}
/* Error if no key pair found. */
if (ISC_LIST_EMPTY(rdatalist->rdata)) {
fatal("no %s/%s %s key pair found for bundle %s", namestr,
algstr, rolestr, timestr);
fatal("no %s/%s zsk key pair found for bundle %s", namestr,
algstr, timestr);
}
/* All good, print DNSKEY RRset. */
@@ -611,8 +600,8 @@ fail:
freerrset(&rdataset);
if (ret != ISC_R_SUCCESS) {
fatal("failed to print %s/%s %s key pair found for bundle %s",
namestr, algstr, rolestr, timestr);
fatal("failed to print %s/%s zsk key pair found for bundle %s",
namestr, algstr, timestr);
}
return (next_bundle);
@@ -690,14 +679,25 @@ sign_rrset(ksr_ctx_t *ksr, isc_stdtime_t inception, isc_stdtime_t expiration,
freerrset(&rrsigset);
}
/*
* Create the DNSKEY, CDS, and CDNSKEY records beloing to the KSKs
* listed in 'keys'.
*/
static void
create_cds(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys,
dns_rdataset_t *cdnskeyset, dns_rdataset_t *cdsset) {
create_ksk(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys,
dns_rdataset_t *dnskeyset, dns_rdataset_t *cdnskeyset,
dns_rdataset_t *cdsset) {
dns_rdatalist_t *dnskeylist = isc_mem_get(mctx, sizeof(*dnskeylist));
dns_rdatalist_t *cdnskeylist = isc_mem_get(mctx, sizeof(*cdnskeylist));
dns_rdatalist_t *cdslist = isc_mem_get(mctx, sizeof(*cdslist));
isc_result_t ret = ISC_R_SUCCESS;
dns_kasp_digestlist_t digests = dns_kasp_digests(kasp);
dns_rdatalist_init(dnskeylist);
dnskeylist->rdclass = dns_rdataclass_in;
dnskeylist->type = dns_rdatatype_dnskey;
dnskeylist->ttl = ksr->ttl;
dns_rdatalist_init(cdnskeylist);
cdnskeylist->rdclass = dns_rdataclass_in;
cdnskeylist->type = dns_rdatatype_cdnskey;
@@ -712,17 +712,37 @@ create_cds(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys,
dk = ISC_LIST_NEXT(dk, link))
{
isc_buffer_t buf;
isc_buffer_t *newbuf = NULL;
dns_rdata_t *rdata = NULL;
isc_buffer_t *newbuf;
dns_rdata_t *rdata;
isc_region_t r;
isc_region_t rcds;
unsigned char rdatabuf[DST_KEY_MAXSIZE];
unsigned char kskbuf[DST_KEY_MAXSIZE];
unsigned char cdnskeybuf[DST_KEY_MAXSIZE];
unsigned char cdsbuf[DNS_DS_BUFFERSIZE];
/* KSK */
newbuf = NULL;
rdata = isc_mem_get(mctx, sizeof(*rdata));
dns_rdata_init(rdata);
isc_buffer_init(&buf, rdatabuf, sizeof(rdatabuf));
isc_buffer_init(&buf, kskbuf, sizeof(kskbuf));
CHECK(dst_key_todns(dk->key, &buf));
isc_buffer_usedregion(&buf, &r);
isc_buffer_allocate(mctx, &newbuf, r.length);
isc_buffer_putmem(newbuf, r.base, r.length);
isc_buffer_usedregion(newbuf, &r);
dns_rdata_fromregion(rdata, dns_rdataclass_in,
dns_rdatatype_dnskey, &r);
ISC_LIST_APPEND(dnskeylist->rdata, rdata, link);
ISC_LIST_APPEND(cleanup_list, newbuf, link);
isc_buffer_clear(newbuf);
/* CDNSKEY */
newbuf = NULL;
rdata = isc_mem_get(mctx, sizeof(*rdata));
dns_rdata_init(rdata);
isc_buffer_init(&buf, cdnskeybuf, sizeof(cdnskeybuf));
CHECK(dst_key_todns(dk->key, &buf));
isc_buffer_usedregion(&buf, &r);
isc_buffer_allocate(mctx, &newbuf, r.length);
@@ -736,6 +756,7 @@ create_cds(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys,
ISC_LIST_APPEND(cleanup_list, newbuf, link);
isc_buffer_clear(newbuf);
/* CDS */
for (dns_kasp_digest_t *alg = ISC_LIST_HEAD(digests);
alg != NULL; alg = ISC_LIST_NEXT(alg, link))
{
@@ -765,12 +786,13 @@ create_cds(ksr_ctx_t *ksr, dns_kasp_t *kasp, dns_dnsseckeylist_t *keys,
}
}
/* All good */
dns_rdatalist_tordataset(dnskeylist, dnskeyset);
dns_rdatalist_tordataset(cdnskeylist, cdnskeyset);
dns_rdatalist_tordataset(cdslist, cdsset);
return;
fail:
fatal("failed to create CDS/CDNSKEY");
fatal("failed to create KSK/CDS/CDNSKEY");
}
static void
@@ -956,6 +978,11 @@ request(ksr_ctx_t *ksr) {
* or withdrawal of a key that is after the current
* inception.
*/
if (dns_kasp_key_ksk(kk)) {
/* We only want ZSKs in the request. */
continue;
}
next = print_dnskeys(kk, ksr->ttl, &keys, inception,
next);
}
@@ -977,6 +1004,7 @@ sign(ksr_ctx_t *ksr) {
dns_dnsseckeylist_t keys;
dns_kasp_t *kasp = NULL;
dns_rdatalist_t *rdatalist = NULL;
dns_rdataset_t ksk = DNS_RDATASET_INIT;
dns_rdataset_t cdnskey = DNS_RDATASET_INIT;
dns_rdataset_t cds = DNS_RDATASET_INIT;
isc_result_t ret;
@@ -1011,8 +1039,8 @@ sign(ksr_ctx_t *ksr) {
isc_result_totext(ret));
}
/* CDS and CDNSKEY */
create_cds(ksr, kasp, &keys, &cdnskey, &cds);
/* KSK, CDS and CDNSKEY */
create_ksk(ksr, kasp, &keys, &ksk, &cdnskey, &cds);
for (ret = isc_lex_gettoken(lex, opt, &token); ret == ISC_R_SUCCESS;
ret = isc_lex_gettoken(lex, opt, &token))
@@ -1073,7 +1101,16 @@ sign(ksr_ctx_t *ksr) {
dns_rdatalist_init(rdatalist);
rdatalist->rdclass = dns_rdataclass_in;
rdatalist->type = dns_rdatatype_dnskey;
rdatalist->ttl = TTL_MAX;
rdatalist->ttl = ksr->ttl;
for (isc_result_t r = dns_rdatalist_first(&ksk);
r == ISC_R_SUCCESS; r = dns_rdatalist_next(&ksk))
{
dns_rdata_t *clone =
isc_mem_get(mctx, sizeof(*clone));
dns_rdata_init(clone);
dns_rdatalist_current(&ksk, clone);
ISC_LIST_APPEND(rdatalist->rdata, clone, link);
}
inception = next_inception;
have_bundle = true;
@@ -1091,7 +1128,7 @@ sign(ksr_ctx_t *ksr) {
} while (token.type != isc_tokentype_eol);
} else {
/* Parse DNSKEY */
dns_ttl_t ttl = TTL_MAX;
dns_ttl_t ttl = ksr->ttl;
isc_buffer_t buf;
isc_buffer_t *newbuf = NULL;
dns_rdata_t *rdata = NULL;
@@ -1146,8 +1183,9 @@ sign(ksr_ctx_t *ksr) {
fail:
/* Clean up */
freerrset(&cds);
freerrset(&ksk);
freerrset(&cdnskey);
freerrset(&cds);
isc_lex_destroy(&lex);
cleanup(&keys, kasp);

View File

@@ -113,11 +113,14 @@ Commands
.. option:: request
Create a Key Signing Request (KSR), given a DNSSEC policy and an interval.
This will generate a file with a number of key bundles, where each bundle
contains the currently published ZSKs (according to the timing metadata).
.. option:: sign
Sign a Key Signing Request (KSR), given a DNSSEC policy and an interval,
creating a Signed Key Response (SKR).
creating a Signed Key Response (SKR). This will add the corresponding DNSKEY,
CDS, and CDNSKEY records for the KSK that is being used for signing.
Exit Status
~~~~~~~~~~~

View File

@@ -190,19 +190,16 @@ ksr common -i $now -e +1y request common.test >ksr.request.out.$n 2>&1 || ret=1
key=$(cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk1.id)
inception=$(cat $key.state | grep "Generated" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >ksr.request.expect.$n
cat common.test.ksk1 >>ksr.request.expect.$n
cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk1 >>ksr.request.expect.$n
# Bundle 2: KSK + ZSK1 + ZSK2
key=$(cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk2.id)
inception=$(cat $key.state | grep "Published" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat common.test.ksk1 >>ksr.request.expect.$n
print_dnskeys common.test 1 2 $DEFAULT_ALGORITHM_NUMBER ksr.keygen.out.expect
# Bundle 3: KSK + ZSK2
key=$(cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk1.id)
inception=$(cat $key.state | grep "Removed" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat common.test.ksk1 >>ksr.request.expect.$n
cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk2 >>ksr.request.expect.$n
# Footer
cp ksr.request.expect.$n ksr.request.expect.base
@@ -249,7 +246,7 @@ _update_expected_zsks() {
fi
}
check_ksr() {
check_skr() {
_ret=0
zone=$1
file=$2
@@ -261,7 +258,7 @@ check_ksr() {
cds4=$($DSFROMKEY -T 3600 -a SHA-384 -C -w $(cat "${zone}.ksk1.id"))
cdnskey=$(awk '{sub(/DNSKEY/,"CDNSKEY")}1' <${zone}.ksk1)
echo_i "check ksr: zone $1 file $2 from $3 to $4 num-zsk $5"
echo_i "check skr: zone $1 file $2 from $3 to $4 num-zsk $5"
# Initial state: not in a rollover, expect a SignedKeyResponse header
# on the first line, start with the first ZSK (set zsk=0 so when we
@@ -273,7 +270,7 @@ check_ksr() {
rollover_done=$start
_update_expected_zsks
echo_i "check ksr: inception $inception rollover-start $rollover_start rollover-done $rollover_done"
echo_i "check skr: inception $inception rollover-start $rollover_start rollover-done $rollover_done"
lineno=0
complete=0
@@ -435,7 +432,7 @@ check_ksr() {
zsk1=$(cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk1.id)
start=$(cat $zsk1.state | grep "Generated" | awk '{print $2}')
end=$(addtime $start 31536000) # one year
check_ksr "common.test" "ksr.sign.out.$n" $start $end 2 || ret=1
check_skr "common.test" "ksr.sign.out.$n" $start $end 2 || ret=1
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
@@ -500,25 +497,21 @@ cp ksr.request.expect.base ksr.request.expect.$n
key=$(cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk3.id)
inception=$(cat $key.state | grep "Published" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat common.test.ksk1 >>ksr.request.expect.$n
print_dnskeys common.test 2 3 $DEFAULT_ALGORITHM_NUMBER ksr.keygen.out.expect
# Bundle 5: KSK + ZSK3
key=$(cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk2.id)
inception=$(cat $key.state | grep "Removed" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat common.test.ksk1 >>ksr.request.expect.$n
cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk3 >>ksr.request.expect.$n
# Bundle 6: KSK + ZSK3 + ZSK4
key=$(cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk4.id)
inception=$(cat $key.state | grep "Published" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat common.test.ksk1 >>ksr.request.expect.$n
print_dnskeys common.test 3 4 $DEFAULT_ALGORITHM_NUMBER ksr.keygen.out.expect
# Bundle 7: KSK + ZSK4
key=$(cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk3.id)
inception=$(cat $key.state | grep "Removed" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat common.test.ksk1 >>ksr.request.expect.$n
cat common.test.$DEFAULT_ALGORITHM_NUMBER.zsk4 >>ksr.request.expect.$n
# Footer
cp ksr.request.expect.$n ksr.request.expect.base
@@ -545,7 +538,7 @@ ret=0
ksr common -i $now -e +2y -K offline -f ksr.request.expect sign common.test >ksr.sign.out.$n 2>&1 || ret=1
start=$(cat $zsk1.state | grep "Generated" | awk '{print $2}')
end=$(addtime $start 63072000) # two years
check_ksr "common.test" "ksr.sign.out.$n" $start $end 4 || ret=1
check_skr "common.test" "ksr.sign.out.$n" $start $end 4 || ret=1
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
@@ -593,7 +586,6 @@ ksr unlimited -i $created -e +4y request unlimited.test >ksr.request.out.$n 2>&1
# Only one bundle: KSK + ZSK
inception=$(cat $key.state | grep "Generated" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >ksr.request.expect.$n
cat unlimited.test.ksk1 >>ksr.request.expect.$n
cat unlimited.test.$DEFAULT_ALGORITHM_NUMBER.zsk1 >>ksr.request.expect.$n
# Footer
grep ";; KeySigningRequest 1.0 generated at" ksr.request.out.$n >footer.$n || ret=1
@@ -611,7 +603,7 @@ ret=0
ksr unlimited -i $created -e +4y -K offline -f ksr.request.expect sign unlimited.test >ksr.sign.out.$n 2>&1 || ret=1
start=$(cat $key.state | grep "Generated" | awk '{print $2}')
end=$(addtime $start 126144000) # four years
check_ksr "unlimited.test" "ksr.sign.out.$n" $start $end 1 || ret=1
check_skr "unlimited.test" "ksr.sign.out.$n" $start $end 1 || ret=1
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
@@ -626,7 +618,7 @@ CDNSKEY="no"
CDS_SHA1="yes"
CDS_SHA256="yes"
CDS_SHA384="yes"
check_ksr "unlimited.test" "ksr.sign.out.$n" $start $end 1 || ret=1
check_skr "unlimited.test" "ksr.sign.out.$n" $start $end 1 || ret=1
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
@@ -641,7 +633,7 @@ CDNSKEY="yes"
CDS_SHA1="no"
CDS_SHA256="no"
CDS_SHA384="no"
check_ksr "unlimited.test" "ksr.sign.out.$n" $start $end 1 || ret=1
check_skr "unlimited.test" "ksr.sign.out.$n" $start $end 1 || ret=1
test "$ret" -eq 0 || echo_i "failed"
status=$((status + ret))
@@ -695,40 +687,30 @@ ksr two-tone -i $created -e +6mo request two-tone.test >ksr.request.out.$n 2>&1
key=$(cat two-tone.test.$DEFAULT_ALGORITHM_NUMBER.zsk1.id)
inception=$(cat $key.state | grep "Generated" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >ksr.request.expect.$n
cat two-tone.test.ksk1 >>ksr.request.expect.$n
cat two-tone.test.ksk2 >>ksr.request.expect.$n
cat two-tone.test.$DEFAULT_ALGORITHM_NUMBER.zsk1 >>ksr.request.expect.$n
cat two-tone.test.$ALTERNATIVE_ALGORITHM_NUMBER.zsk1 >>ksr.request.expect.$n
# Bundle 2: KSK-A1, KSK-B1, ZSK-A1 + ZSK-A2, ZSK-B1
key=$(cat two-tone.test.$DEFAULT_ALGORITHM_NUMBER.zsk2.id)
inception=$(cat $key.state | grep "Published" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat two-tone.test.ksk1 >>ksr.request.expect.$n
cat two-tone.test.ksk2 >>ksr.request.expect.$n
print_dnskeys two-tone.test 1 2 $DEFAULT_ALGORITHM_NUMBER ksr.keygen.out.expect.$DEFAULT_ALGORITHM_NUMBER >>ksr.request.expect.$n
cat two-tone.test.$ALTERNATIVE_ALGORITHM_NUMBER.zsk1 >>ksr.request.expect.$n
# Bundle 3: KSK-A1, KSK-B1, ZSK-A2, ZSK-B1
key=$(cat two-tone.test.$DEFAULT_ALGORITHM_NUMBER.zsk1.id)
inception=$(cat $key.state | grep "Removed" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat two-tone.test.ksk1 >>ksr.request.expect.$n
cat two-tone.test.ksk2 >>ksr.request.expect.$n
cat two-tone.test.$DEFAULT_ALGORITHM_NUMBER.zsk2 >>ksr.request.expect.$n
cat two-tone.test.$ALTERNATIVE_ALGORITHM_NUMBER.zsk1 >>ksr.request.expect.$n
# Bundle 4: KSK-A1, KSK-B1, ZSK-A2, ZSK-B1 + ZSK-B2
key=$(cat two-tone.test.$ALTERNATIVE_ALGORITHM_NUMBER.zsk2.id)
inception=$(cat $key.state | grep "Published" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat two-tone.test.ksk1 >>ksr.request.expect.$n
cat two-tone.test.ksk2 >>ksr.request.expect.$n
cat two-tone.test.$DEFAULT_ALGORITHM_NUMBER.zsk2 >>ksr.request.expect.$n
print_dnskeys two-tone.test 1 2 $ALTERNATIVE_ALGORITHM_NUMBER ksr.keygen.out.expect.$ALTERNATIVE_ALGORITHM_NUMBER >>ksr.request.expect.$n
# Bundle 5: KSK-A1, KSK-B1, ZSK-A2, ZSK-B2
key=$(cat two-tone.test.$ALTERNATIVE_ALGORITHM_NUMBER.zsk1.id)
inception=$(cat $key.state | grep "Removed" | cut -d' ' -f 2-)
echo ";; KeySigningRequest 1.0 $inception" >>ksr.request.expect.$n
cat two-tone.test.ksk1 >>ksr.request.expect.$n
cat two-tone.test.ksk2 >>ksr.request.expect.$n
cat two-tone.test.$DEFAULT_ALGORITHM_NUMBER.zsk2 >>ksr.request.expect.$n
cat two-tone.test.$ALTERNATIVE_ALGORITHM_NUMBER.zsk2 >>ksr.request.expect.$n
# Footer