+17 September 2007: Wouter
+ - NSEC3 hash cache unit test.
+
14 September 2007: Wouter
- nsec3 nodata proof, nods proof, wildcard proof.
- nsec3 support for cname chain ending in noerror or nodata.
#include "testcode/unitmain.h"
#include "validator/val_sigcrypt.h"
#include "validator/val_nsec.h"
+#include "validator/val_nsec3.h"
#include "validator/validator.h"
#include "testcode/ldns-testpkts.h"
#include "util/data/msgreply.h"
#include "util/data/msgparse.h"
+#include "util/data/dname.h"
#include "util/region-allocator.h"
#include "util/alloc.h"
+#include "util/rbtree.h"
#include "util/net_help.h"
#include "util/module.h"
#include "util/config_file.h"
struct edns_data edns;
entry_to_buf(e, pkt);
ret = reply_info_parse(pkt, alloc, qi, rep, region, &edns);
- region_free_all(region);
if(ret != 0) {
printf("parse code %d: %s\n", ret,
ldns_lookup_by_id(ldns_rcodes, ret)->name);
printf("result(no)= %s\n", ret?"yes":"no");
}
unit_assert(!ret);
+ verbose(VERB_DETAIL, "DS fail: OK; matched unit test");
} else {
fatal_exit("Bad qname in DS unit test, yes or no");
}
unit_assert(!unitest_nsec_has_type_rdata(bitmap, len, 2230));
}
+/** Test hash algo - NSEC3 hash it and compare result */
+static void
+nsec3_hash_test_entry(struct entry* e, rbtree_t* ct,
+ struct alloc_cache* alloc, struct region* region,
+ ldns_buffer* buf)
+{
+ struct query_info qinfo;
+ struct reply_info* rep = NULL;
+ struct ub_packed_rrset_key* answer, *nsec3;
+ struct nsec3_cached_hash* hash;
+ int ret;
+ uint8_t* qname;
+
+ if(vsig) {
+ printf("verifying NSEC3 hash:\n");
+ ldns_pkt_print(stdout, e->reply_list->reply);
+ printf("\n");
+ }
+ entry_to_repinfo(e, alloc, region, buf, &qinfo, &rep);
+ nsec3 = find_rrset_type(rep, LDNS_RR_TYPE_NSEC3);
+ answer = find_rrset_type(rep, LDNS_RR_TYPE_AAAA);
+ qname = region_alloc_init(region, qinfo.qname, qinfo.qname_len);
+ /* check test is OK */
+ unit_assert(nsec3 && answer && qname);
+
+ ret = nsec3_hash_name(ct, region, buf, nsec3, 0, qname,
+ qinfo.qname_len, &hash);
+ if(ret != 1) {
+ printf("Bad nsec3_hash_name retcode %d\n", ret);
+ unit_assert(ret == 1);
+ }
+ unit_assert(hash->dname && hash->hash && hash->hash_len &&
+ hash->b32 && hash->b32_len);
+ unit_assert(hash->b32_len == (size_t)answer->rk.dname[0]);
+ /* does not do lowercasing. */
+ unit_assert(memcmp(hash->b32, answer->rk.dname+1, hash->b32_len)
+ == 0);
+
+ reply_info_parsedelete(rep, alloc);
+ query_info_clear(&qinfo);
+}
+
+
+/** Read file to test NSEC3 hash algo */
+static void
+nsec3_hash_test(const char* fname)
+{
+ /*
+ * The list contains a list of ldns-testpkts entries.
+ * Every entry is a test.
+ * The qname is hashed.
+ * The answer section AAAA RR name is the required result.
+ * The auth section NSEC3 is used to get hash parameters.
+ * The hash cache is maintained per file.
+ *
+ * The test does not perform canonicalization during the compare.
+ */
+ rbtree_t ct;
+ struct region* region = region_create(malloc, free);
+ struct alloc_cache alloc;
+ ldns_buffer* buf = ldns_buffer_new(65535);
+ struct entry* e;
+ struct entry* list = read_datafile(fname);
+
+ if(!list)
+ fatal_exit("could not read %s: %s", fname, strerror(errno));
+ rbtree_init(&ct, &nsec3_hash_cmp);
+ alloc_init(&alloc, NULL, 1);
+ unit_assert(region && buf);
+
+ /* ready to go! */
+ for(e = list; e; e = e->next) {
+ nsec3_hash_test_entry(e, &ct, &alloc, region, buf);
+ }
+
+ delete_entry(list);
+ region_destroy(region);
+ alloc_clear(&alloc);
+ ldns_buffer_free(buf);
+}
+
void
verify_test()
{
verifytest_file("testdata/test_signatures.1", "20070818005004");
dstest_file("testdata/test_ds_sig.1");
nsectest();
+ nsec3_hash_test("testdata/test_nsec3_hash.1");
}
--- /dev/null
+;
+; NSEC3 hash algo test file.
+; The hash cache is maintained for the duration of the file.
+; Every entry is a hash test.
+; query name is hashed.
+; answer AAAA record hash the correct hashed answer name.
+; auth NSEC3 record has the hash parameters.
+;
+
+
+; These are from the nsec3-draft-11 example zone.
+; H(example) = 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom
+ENTRY_BEGIN
+SECTION QUESTION
+example. IN AAAA
+SECTION ANSWER
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(a.example) = 35mthgpgcu1qg68fab165klnsnk3dpvl
+ENTRY_BEGIN
+SECTION QUESTION
+a.example. IN AAAA
+SECTION ANSWER
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(ai.example) = gjeqe526plbf1g8mklp59enfd789njgi
+ENTRY_BEGIN
+SECTION QUESTION
+ai.example. IN AAAA
+SECTION ANSWER
+gjeqe526plbf1g8mklp59enfd789njgi.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(ns1.example) = 2t7b4g4vsa5smi47k61mv5bv1a22bojr
+ENTRY_BEGIN
+SECTION QUESTION
+ns1.example. IN AAAA
+SECTION ANSWER
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(ns2.example) = q04jkcevqvmu85r014c7dkba38o0ji5r
+ENTRY_BEGIN
+SECTION QUESTION
+ns2.example. IN AAAA
+SECTION ANSWER
+q04jkcevqvmu85r014c7dkba38o0ji5r.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(w.example) = k8udemvp1j2f7eg6jebps17vp3n8i58h
+ENTRY_BEGIN
+SECTION QUESTION
+w.example. IN AAAA
+SECTION ANSWER
+k8udemvp1j2f7eg6jebps17vp3n8i58h.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(*.w.example) = r53bq7cc2uvmubfu5ocmm6pers9tk9en
+ENTRY_BEGIN
+SECTION QUESTION
+*.w.example. IN AAAA
+SECTION ANSWER
+r53bq7cc2uvmubfu5ocmm6pers9tk9en.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(x.w.example) = b4um86eghhds6nea196smvmlo4ors995
+ENTRY_BEGIN
+SECTION QUESTION
+x.w.example. IN AAAA
+SECTION ANSWER
+b4um86eghhds6nea196smvmlo4ors995.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(y.w.example) = ji6neoaepv8b5o6k4ev33abha8ht9fgc
+ENTRY_BEGIN
+SECTION QUESTION
+y.w.example. IN AAAA
+SECTION ANSWER
+ji6neoaepv8b5o6k4ev33abha8ht9fgc.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(x.y.w.example) = 2vptu5timamqttgl4luu9kg21e0aor3s
+ENTRY_BEGIN
+SECTION QUESTION
+x.y.w.example. IN AAAA
+SECTION ANSWER
+2vptu5timamqttgl4luu9kg21e0aor3s.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; H(xx.example) = t644ebqk9bibcna874givr6joj62mlhv
+; capitalization changed.
+ENTRY_BEGIN
+SECTION QUESTION
+xX.example. IN AAAA
+SECTION ANSWER
+t644ebqk9bibcna874givr6joj62mlhv.example. AAAA ::1
+SECTION AUTHORITY
+b4um86eghhds6nea196smvmlo4ors995.example. NSEC3 1 1 12 aabbccdd (gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG )
+ENTRY_END
+
+; H(2t7b4g4vsa5smi47k61mv5bv1a22bojr.example)
+; = kohar7mbb8dc2ce8a9qvl8hon4k53uhi
+ENTRY_BEGIN
+SECTION QUESTION
+2t7b4g4vsa5smi47k61mv5bv1a22bojr.example. IN AAAA
+SECTION ANSWER
+kohar7mbb8dc2ce8a9qvl8hon4k53uhi.example. AAAA ::1
+SECTION AUTHORITY
+b4um86eghhds6nea196smvmlo4ors995.example. NSEC3 1 1 12 aabbccdd (gjeqe526plbf1g8mklp59enfd789njgi MX RRSIG )
+ENTRY_END
+
+
+
+; repeat entry to test the cache.
+; H(example) = 0p9mhaveqvm6t7vbl5lop2u3t2rp3tom
+ENTRY_BEGIN
+SECTION QUESTION
+example. IN AAAA
+SECTION ANSWER
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; repeat entry to test the cache.
+; H(a.example) = 35mthgpgcu1qg68fab165klnsnk3dpvl
+ENTRY_BEGIN
+SECTION QUESTION
+a.example. IN AAAA
+SECTION ANSWER
+35mthgpgcu1qg68fab165klnsnk3dpvl.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; repeat entry to test the cache.
+; H(ai.example) = gjeqe526plbf1g8mklp59enfd789njgi
+ENTRY_BEGIN
+SECTION QUESTION
+ai.example. IN AAAA
+SECTION ANSWER
+gjeqe526plbf1g8mklp59enfd789njgi.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
+; repeat entry to test the cache.
+; capitalization of qname.
+; H(ai.example) = gjeqe526plbf1g8mklp59enfd789njgi
+ENTRY_BEGIN
+SECTION QUESTION
+AI.example. IN AAAA
+SECTION ANSWER
+gjeqe526plbf1g8mklp59enfd789njgi.example. AAAA ::1
+SECTION AUTHORITY
+0p9mhaveqvm6t7vbl5lop2u3t2rp3tom.example. NSEC3 1 1 12 aabbccdd (2t7b4g4vsa5smi47k61mv5bv1a22bojr MX DNSKEY NS SOA NSEC3PARAM RRSIG )
+ENTRY_END
+
int b32_pton_extended_hex(char const *src, size_t hashed_owner_str_len,
uint8_t *target, size_t targsize);
-/**
- * The NSEC3 hash result storage.
- * Consists of an rbtree, with these nodes in it.
- * The nodes detail how a set of parameters (from nsec3 rr) plus
- * a dname result in a hash.
- */
-struct nsec3_cached_hash {
- /** rbtree node, key is this structure */
- rbnode_t node;
- /** where are the parameters for conversion, in this rrset data */
- struct ub_packed_rrset_key* nsec3;
- /** where are the parameters for conversion, this RR number in data */
- int rr;
- /** the name to convert */
- uint8_t* dname;
- /** length of the dname */
- size_t dname_len;
- /** the hash result (not base32 encoded) */
- uint8_t* hash;
- /** length of hash in bytes */
- size_t hash_len;
- /** the hash result in base32 encoding */
- uint8_t* b32;
- /** length of base32 encoding (as a label) */
- size_t b32_len;
-};
-
/**
* Closest encloser (ce) proof results
* Contains the ce and the next-closer (nc) proof.
}
/** nsec3_cache_compare for rbtree */
-static int
+int
nsec3_hash_cmp(const void* c1, const void* c2)
{
struct nsec3_cached_hash* h1 = (struct nsec3_cached_hash*)c1;
(unsigned long)ldns_buffer_limit(buf),
(unsigned char*)c->hash);
}
+ break;
#endif /* SHA_DIGEST_LENGTH */
default:
log_err("nsec3 hash of unknown algo %d", algo);
return 1;
}
-/**
- * Obtain the hash of an owner name.
- * @param table: the cache table. Must be inited at start.
- * @param region: scratch region to use for allocation.
- * @param buf: temporary buffer.
- * @param nsec3: the rrset with parameters
- * @param rr: rr number from d that has the NSEC3 parameters to hash to.
- * @param dname: name to hash
- * @param dname_len: the length of the name.
- * @param hash: the hash node is returned on success.
- * @return:
- * 1 on success, either from cache or newly hashed hash is returned.
- * 0 on a malloc failure.
- * -1 if the NSEC3 rr was badly formatted (i.e. formerr).
- */
-static int
+int
nsec3_hash_name(rbtree_t* table, struct region* region, ldns_buffer* buf,
struct ub_packed_rrset_key* nsec3, int rr, uint8_t* dname,
size_t dname_len, struct nsec3_cached_hash** hash)
#ifndef VALIDATOR_VAL_NSEC3_H
#define VALIDATOR_VAL_NSEC3_H
+#include "util/rbtree.h"
struct val_env;
+struct region;
struct module_env;
struct ub_packed_rrset_key;
enum sec_status;
struct ub_packed_rrset_key** list, size_t num,
struct query_info* qinfo, struct key_entry_key* kkey, int* nodata);
+/**
+ * The NSEC3 hash result storage.
+ * Consists of an rbtree, with these nodes in it.
+ * The nodes detail how a set of parameters (from nsec3 rr) plus
+ * a dname result in a hash.
+ */
+struct nsec3_cached_hash {
+ /** rbtree node, key is this structure */
+ rbnode_t node;
+ /** where are the parameters for conversion, in this rrset data */
+ struct ub_packed_rrset_key* nsec3;
+ /** where are the parameters for conversion, this RR number in data */
+ int rr;
+ /** the name to convert */
+ uint8_t* dname;
+ /** length of the dname */
+ size_t dname_len;
+ /** the hash result (not base32 encoded) */
+ uint8_t* hash;
+ /** length of hash in bytes */
+ size_t hash_len;
+ /** the hash result in base32 encoding */
+ uint8_t* b32;
+ /** length of base32 encoding (as a label) */
+ size_t b32_len;
+};
+
+/**
+ * Rbtree for hash cache comparison function
+ */
+int nsec3_hash_cmp(const void* c1, const void* c2);
+
+/**
+ * Obtain the hash of an owner name.
+ * Used internally by the nsec3 proof functions in this file.
+ * published to enable unit testing of hash algorithms and cache.
+ *
+ * @param table: the cache table. Must be inited at start.
+ * @param region: scratch region to use for allocation.
+ * This region holds the tree, if you wipe the region, reinit the tree.
+ * @param buf: temporary buffer.
+ * @param nsec3: the rrset with parameters
+ * @param rr: rr number from d that has the NSEC3 parameters to hash to.
+ * @param dname: name to hash
+ * This pointer is used inside the tree, assumed region-alloced.
+ * @param dname_len: the length of the name.
+ * @param hash: the hash node is returned on success.
+ * @return:
+ * 1 on success, either from cache or newly hashed hash is returned.
+ * 0 on a malloc failure.
+ * -1 if the NSEC3 rr was badly formatted (i.e. formerr).
+ */
+int nsec3_hash_name(rbtree_t* table, struct region* region, ldns_buffer* buf,
+ struct ub_packed_rrset_key* nsec3, int rr, uint8_t* dname,
+ size_t dname_len, struct nsec3_cached_hash** hash);
+
#endif /* VALIDATOR_VAL_NSEC3_H */