Support for DST_ALG_PRIVATEDNS and DST_ALG_PRIVATEOID

The algorithm values PRIVATEDNS and PRIVATEOID are placeholders,
signifying that the actual algorithm identifier is encoded into the
key data. Keys using this mechanism are now supported.

- The algorithm values PRIVATEDNS and PRIVATEOID cannot be used to
  build a key file name; dst_key_buildfilename() will assert if
  they are used.

- The DST key values for private algorithms are higher than 255.
  Since DST_ALG_MAXALG now exceeds 256, algorithm arrays that were
  previously hardcoded to size 256 have been resized.

- New mnemonic/text conversion functions have been added.
  dst_algorithm_{fromtext,totext,format} can handle algorithm
  identifiers encoded in PRIVATEDNS and PRIVATEOID keys, as well
  as the traditional algorithm identifiers. (Note: The existing
  dns_secalg_{fromtext,totext,format} functions are similar, but
  do *not* support PRIVATEDNS and PRIVATEOID. In most cases, the
  new functions have taken the place of the old ones, but in a few
  cases the old version is still appropriate.)

- dns_private{oid,dns}_{fromtext,totext,format} converts between
  DST algorithm values and the mnemonic strings for algorithms
  implemented using PRIVATEDNS or PRIVATEOID. (E.g., "RSASHA256OID").

- dst_algorithm_tosecalg() returns the DNSSEC algorithm identifier
  that applies for a given DST algorithm.  For PRIVATEDNS- or
  PRIVATEOID- based algorithms, the result will be PRIVATEDNS or
  PRIVATEOID, respectively.

- dst_algorithm_fromprivatedns() and dst_algorithm_fromprivateoid()
  return the DST algorithm identifier for an encoded algorithm in
  wire format, represented as in DNS name or an object identifier,
  respectively.

- dst_algorithm_fromdata() is a front-end for the above; it extracts
  the private algorithm identifier encoded at the begining of a
  block of key or signature data, and returns the matching DST
  algorithm number.

- dst_key_fromdns() and dst_key_frombuffer() now work with keys
  that have PRIVATEDNS and PRIVATEOID algorithm identifiers at the
  beginning.
This commit is contained in:
Mark Andrews 2025-04-16 11:31:41 +10:00
parent e6f1363964
commit 6fe09d85ab
8 changed files with 303 additions and 15 deletions

View file

@ -2107,6 +2107,8 @@ buildfilename(dns_name_t *name, dns_keytag_t id, unsigned int alg,
isc_result_t result;
REQUIRE(out != NULL);
REQUIRE(alg != 0 && alg != DST_ALG_PRIVATEOID &&
alg != DST_ALG_PRIVATEDNS);
if ((type & DST_TYPE_PRIVATE) != 0) {
suffix = ".private";
@ -2172,6 +2174,22 @@ frombuffer(const dns_name_t *name, unsigned int alg, unsigned int flags,
REQUIRE(mctx != NULL);
REQUIRE(keyp != NULL && *keyp == NULL);
if (alg == DNS_KEYALG_PRIVATEDNS) {
isc_buffer_t b = *source;
alg = dst_algorithm_fromprivatedns(&b);
if (alg == 0) {
return DST_R_UNSUPPORTEDALG;
}
}
if (alg == DNS_KEYALG_PRIVATEOID) {
isc_buffer_t b = *source;
alg = dst_algorithm_fromprivateoid(&b);
if (alg == 0) {
return DST_R_UNSUPPORTEDALG;
}
}
key = get_key_struct(name, alg, flags, protocol, 0, rdclass, 0, mctx);
if (isc_buffer_remaininglength(source) > 0) {
@ -2636,3 +2654,67 @@ dst_hmac_algorithm_totext(dst_algorithm_t alg) {
return "unknown";
}
}
dns_secalg_t
dst_algorithm_tosecalg(dst_algorithm_t dst_alg) {
static dns_secalg_t dns_alg[DST_MAX_ALGS] = { 0 };
if (dst_alg < 256) {
return dst_alg;
}
if (dst_alg < DST_MAX_ALGS) {
return dns_alg[dst_alg];
}
return 0;
}
dst_algorithm_t
dst_algorithm_fromprivatedns(isc_buffer_t *buffer) {
dns_fixedname_t fixed;
dns_name_t *name = dns_fixedname_initname(&fixed);
isc_result_t result;
result = dns_name_fromwire(name, buffer, DNS_DECOMPRESS_DEFAULT, NULL);
if (result != ISC_R_SUCCESS) {
return 0;
}
/*
* Do name to dst_algorithm number mapping here.
*/
return 0;
}
dst_algorithm_t
dst_algorithm_fromprivateoid(isc_buffer_t *buffer) {
isc_region_t r;
isc_buffer_remainingregion(buffer, &r);
/*
* Do OID to dst_algorithm number mapping here. There is a
* length byte followed by the OID of that length.
*/
if (r.length > 0 && ((unsigned int)r.base[0] + 1) <= r.length) {
return 0;
}
return 0;
}
dst_algorithm_t
dst_algorithm_fromdata(dns_secalg_t algorithm, unsigned char *data,
unsigned int length) {
isc_buffer_t b;
switch (algorithm) {
case DNS_KEYALG_PRIVATEDNS:
isc_buffer_init(&b, data, length);
isc_buffer_add(&b, length);
return dst_algorithm_fromprivatedns(&b);
case DNS_KEYALG_PRIVATEOID:
isc_buffer_init(&b, data, length);
isc_buffer_add(&b, length);
return dst_algorithm_fromprivateoid(&b);
default:
return algorithm;
}
}

View file

@ -110,7 +110,12 @@ typedef enum dst_algorithm {
DST_ALG_HMAC_LAST = DST_ALG_HMACSHA512,
DST_ALG_INDIRECT = 252,
DST_ALG_PRIVATE = 254,
DST_ALG_PRIVATEDNS = 253,
DST_ALG_PRIVATEOID = 254,
DST_ALG_RESERVED = 255,
/*
* Put PRIVATE DNS and PRIVATE OID identifiers here.
*/
DST_MAX_ALGS = 256,
} dst_algorithm_t;
@ -206,6 +211,20 @@ dst_algorithm_supported(unsigned int alg);
* \li false
*/
dst_algorithm_t
dst_algorithm_fromprivateoid(isc_buffer_t *buffer);
/*
* Extract the dst algorithm identifier that matches
* the OID value found at the start of 'buffer'.
*/
dst_algorithm_t
dst_algorithm_fromprivatedns(isc_buffer_t *buf);
/*
* Extract the dst algorithm identifier that matches
* the DNS name found at the start of 'buffer'.
*/
bool
dst_ds_digest_supported(unsigned int digest_type);
/*%<
@ -1160,3 +1179,89 @@ dst_hmac_algorithm_totext(dst_algorithm_t alg);
* Return the name associtated with the HMAC algorithm 'alg'
* or return "unknown".
*/
isc_result_t
dst_algorithm_fromtext(dst_algorithm_t *algp, isc_textregion_t *source);
/*%<
* Convert the text 'source' refers to into a DST security algorithm value.
* The text may contain either a mnemonic algorithm name or a decimal algorithm
* number. This supports more algorithms than 'dns_secalg_fromtext' as it
* supports private algorithms used with PRIVATEDNS and PRIVATEOID.
*
* Requires:
*\li 'algp' is a valid pointer.
*
*\li 'source' is a valid text region.
*
* Returns:
*\li ISC_R_SUCCESS on success
*\li ISC_R_RANGE numeric type is out of range
*\li DNS_R_UNKNOWN mnemonic type is unknown
*/
isc_result_t
dst_algorithm_totext(dst_algorithm_t alg, isc_buffer_t *target);
/*%<
* Put a textual representation of DST security algorithm 'alg'
* into 'target'. This supports a superset of dns_secalg_totext.
*
* Requires:
*\li 'alg' is a valid dst_algorithm_t.
*
*\li 'target' is a valid text buffer.
*
* Ensures,
* if the result is success:
*\li The used space in 'target' is updated.
*
* Returns:
*\li ISC_R_SUCCESS on success
*\li ISC_R_NOSPACE target buffer is too small
*/
#define DST_ALGORITHM_FORMATSIZE 20
void
dst_algorithm_format(dst_algorithm_t dst_alg, char *data, unsigned int length);
/*%<
* Wrapper for dst_algorithm_totext(), writing text into 'cp'
*/
dns_secalg_t
dst_algorithm_tosecalg(dst_algorithm_t dst_alg);
/*%<
* Return the DNSSEC algorithm identifier that applies for the DST
* algorithm. For PRIVATEDNS and PRIVATEOID based algorithms, this
* is PRIVATEDNS and PRIVATEOID respectively.
*
* Zero is returned when there is no mapping.
*/
isc_result_t
dst_privatedns_fromtext(dst_algorithm_t *algp, isc_textregion_t *source);
isc_result_t
dns_privatedns_totext(dst_algorithm_t alg, isc_buffer_t *b);
void
dns_privatedns_format(dst_algorithm_t alg, char *buf, unsigned int size);
isc_result_t
dst_privateoid_fromtext(dst_algorithm_t *algp, isc_textregion_t *source);
isc_result_t
dns_privateoid_totext(dst_algorithm_t alg, isc_buffer_t *b);
void
dns_privateoid_format(dst_algorithm_t alg, char *buf, unsigned int size);
dst_algorithm_t
dst_algorithm_fromdata(dns_secalg_t algorithm, unsigned char *data,
unsigned int length);
/*%<
* If 'algorithm' is PRIVATEOID or PRIVATEDNS, extract the DNSSEC private
* algorithm encoded at the begining of data and return the DST algorithm
* number that corresponds to it; if the algorithm is unknown to DST,
* return 0.
*
* If 'algorithm' is any other value, return it directly.
*/

View file

@ -36,6 +36,8 @@
#include <dns/secalg.h>
#include <dns/secproto.h>
#include <dst/dst.h>
#define RETERR(x) \
do { \
isc_result_t _r = (x); \
@ -115,6 +117,16 @@
{ DNS_KEYALG_PRIVATEDNS, "PRIVATEDNS", 0 }, \
{ DNS_KEYALG_PRIVATEOID, "PRIVATEOID", 0 }, SENTINEL
/*
* PRIVATEDNS subtypes we support.
*/
#define PRIVATEDNSS /* currently empty */
/*
* PRIVATEOID subtypes we support.
*/
#define PRIVATEOIDS /* currently empty */
/* RFC2535 section 7.1 */
#define SECPROTONAMES \
@ -150,6 +162,9 @@ static struct tbl secalgs[] = { SECALGNAMES };
static struct tbl secprotos[] = { SECPROTONAMES };
static struct tbl hashalgs[] = { HASHALGNAMES };
static struct tbl dsdigests[] = { DSDIGESTNAMES };
static struct tbl privatednss[] = { PRIVATEDNSS SENTINEL };
static struct tbl privateoids[] = { PRIVATEOIDS SENTINEL };
static struct tbl dstalgorithms[] = { PRIVATEDNSS PRIVATEOIDS SECALGNAMES };
static struct keyflag {
const char *name;
@ -353,6 +368,64 @@ dns_secalg_format(dns_secalg_t alg, char *cp, unsigned int size) {
}
}
isc_result_t
dst_privatedns_fromtext(dst_algorithm_t *dstalgp, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, privatednss, 0));
*dstalgp = value;
return ISC_R_SUCCESS;
}
isc_result_t
dns_privatedns_totext(dst_algorithm_t alg, isc_buffer_t *target) {
return dns_mnemonic_totext(alg, target, privatednss);
}
void
dns_privatedns_format(dst_algorithm_t alg, char *cp, unsigned int size) {
isc_buffer_t b;
isc_region_t r;
isc_result_t result;
REQUIRE(cp != NULL && size > 0);
isc_buffer_init(&b, cp, size - 1);
result = dns_privatedns_totext(alg, &b);
isc_buffer_usedregion(&b, &r);
r.base[r.length] = 0;
if (result != ISC_R_SUCCESS) {
r.base[0] = 0;
}
}
isc_result_t
dst_privateoid_fromtext(dst_algorithm_t *dstalgp, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, privateoids, 0));
*dstalgp = value;
return ISC_R_SUCCESS;
}
isc_result_t
dns_privateoid_totext(dst_algorithm_t alg, isc_buffer_t *target) {
return dns_mnemonic_totext(alg, target, privateoids);
}
void
dns_privateoid_format(dst_algorithm_t alg, char *cp, unsigned int size) {
isc_buffer_t b;
isc_region_t r;
isc_result_t result;
REQUIRE(cp != NULL && size > 0);
isc_buffer_init(&b, cp, size - 1);
result = dns_privateoid_totext(alg, &b);
isc_buffer_usedregion(&b, &r);
r.base[r.length] = 0;
if (result != ISC_R_SUCCESS) {
r.base[0] = 0;
}
}
isc_result_t
dns_secproto_fromtext(dns_secproto_t *secprotop, isc_textregion_t *source) {
unsigned int value;
@ -579,3 +652,32 @@ dns_rdataclass_format(dns_rdataclass_t rdclass, char *array,
strlcpy(array, "<unknown>", size);
}
}
isc_result_t
dst_algorithm_fromtext(dst_algorithm_t *dstalgp, isc_textregion_t *source) {
unsigned int value;
RETERR(dns_mnemonic_fromtext(&value, source, dstalgorithms, 255));
*dstalgp = value;
return ISC_R_SUCCESS;
}
isc_result_t
dst_algorithm_totext(dst_algorithm_t alg, isc_buffer_t *target) {
return dns_mnemonic_totext(alg, target, dstalgorithms);
}
void
dst_algorithm_format(dst_algorithm_t alg, char *cp, unsigned int size) {
isc_buffer_t b;
isc_region_t r;
isc_result_t result;
REQUIRE(cp != NULL && size > 0);
isc_buffer_init(&b, cp, size - 1);
result = dst_algorithm_totext(alg, &b);
isc_buffer_usedregion(&b, &r);
r.base[r.length] = 0;
if (result != ISC_R_SUCCESS) {
r.base[0] = 0;
}
}

View file

@ -10593,7 +10593,7 @@ dns_resolver_disable_algorithm(dns_resolver_t *resolver, const dns_name_t *name,
unsigned int alg) {
REQUIRE(VALID_RESOLVER(resolver));
if (alg > 255) {
if (alg >= DST_MAX_ALGS) {
return ISC_R_RANGE;
}

View file

@ -2910,7 +2910,6 @@ check_ds_algs(dns_validator_t *val, dns_name_t *name,
isc_result_t result;
dns_rdata_t dsrdata = DNS_RDATA_INIT;
dns_rdataset_current(rdataset, &dsrdata);
result = dns_rdata_tostruct(&dsrdata, &ds, NULL);
RUNTIME_CHECK(result == ISC_R_SUCCESS);

View file

@ -22804,7 +22804,7 @@ dns_zone_cdscheck(dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version) {
isc_result_t result;
dns_dbnode_t *node = NULL;
dns_rdataset_t dnskey, cds, cdnskey;
unsigned char algorithms[256];
unsigned char algorithms[DST_MAX_ALGS];
unsigned int i;
bool empty = false;

View file

@ -68,14 +68,14 @@ typedef struct vctx {
dns_rdataset_t nsecsigs;
dns_rdataset_t nsec3paramset;
dns_rdataset_t nsec3paramsigs;
unsigned char revoked_ksk[256];
unsigned char revoked_zsk[256];
unsigned char standby_ksk[256];
unsigned char standby_zsk[256];
unsigned char ksk_algorithms[256];
unsigned char zsk_algorithms[256];
unsigned char bad_algorithms[256];
unsigned char act_algorithms[256];
unsigned char revoked_ksk[DST_MAX_ALGS];
unsigned char revoked_zsk[DST_MAX_ALGS];
unsigned char standby_ksk[DST_MAX_ALGS];
unsigned char standby_zsk[DST_MAX_ALGS];
unsigned char ksk_algorithms[DST_MAX_ALGS];
unsigned char zsk_algorithms[DST_MAX_ALGS];
unsigned char bad_algorithms[DST_MAX_ALGS];
unsigned char act_algorithms[DST_MAX_ALGS];
isc_heap_t *expected_chains;
isc_heap_t *found_chains;
} vctx_t;
@ -792,7 +792,7 @@ verifynsec3s(const vctx_t *vctx, const dns_name_t *name,
static isc_result_t
verifyset(vctx_t *vctx, dns_rdataset_t *rdataset, const dns_name_t *name,
dns_dbnode_t *node, dst_key_t **dstkeys, size_t nkeys) {
unsigned char set_algorithms[256] = { 0 };
unsigned char set_algorithms[DST_MAX_ALGS] = { 0 };
char namebuf[DNS_NAME_FORMATSIZE];
char algbuf[DNS_SECALG_FORMATSIZE];
char typebuf[DNS_RDATATYPE_FORMATSIZE];

View file

@ -624,8 +624,8 @@ cfg_kasp_fromconfig(const cfg_obj_t *config, dns_kasp_t *default_kasp,
(void)confget(maps, "keys", &keys);
if (keys != NULL) {
char role[256] = { 0 };
bool warn[256][2] = { { false } };
char role[DST_MAX_ALGS] = { 0 };
bool warn[DST_MAX_ALGS][2] = { { false } };
CFG_LIST_FOREACH (keys, element) {
cfg_obj_t *kobj = cfg_listelt_value(element);