From c7d15770c17c19c61190ca3d336a15359ae15477 Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Mon, 17 Sep 2007 09:25:54 +0000 Subject: [PATCH] nsec3 hash function unit test. git-svn-id: file:///svn/unbound/trunk@615 be551aaa-1e26-0410-a405-d3ace91eadb9 --- doc/Changelog | 3 + testcode/unitverify.c | 87 +++++++++++++++++- testdata/test_nsec3_hash.1 | 180 +++++++++++++++++++++++++++++++++++++ validator/val_nsec3.c | 47 +--------- validator/val_nsec3.h | 58 ++++++++++++ 5 files changed, 330 insertions(+), 45 deletions(-) create mode 100644 testdata/test_nsec3_hash.1 diff --git a/doc/Changelog b/doc/Changelog index be26c6b25..25cc91b8f 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -1,3 +1,6 @@ +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. diff --git a/testcode/unitverify.c b/testcode/unitverify.c index 1a4c358a9..f6359bd73 100644 --- a/testcode/unitverify.c +++ b/testcode/unitverify.c @@ -43,12 +43,15 @@ #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" @@ -91,7 +94,6 @@ entry_to_repinfo(struct entry* e, struct alloc_cache* alloc, struct region* 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); @@ -228,6 +230,7 @@ dstest_entry(struct entry* e, struct alloc_cache* alloc, struct region* 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"); } @@ -362,6 +365,87 @@ nsectest() 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() { @@ -369,4 +453,5 @@ 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"); } diff --git a/testdata/test_nsec3_hash.1 b/testdata/test_nsec3_hash.1 new file mode 100644 index 000000000..7b3813e32 --- /dev/null +++ b/testdata/test_nsec3_hash.1 @@ -0,0 +1,180 @@ +; +; 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 + diff --git a/validator/val_nsec3.c b/validator/val_nsec3.c index e048fef44..6474b85cf 100644 --- a/validator/val_nsec3.c +++ b/validator/val_nsec3.c @@ -66,33 +66,6 @@ int b32_ntop_extended_hex(uint8_t const *src, size_t srclength, 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. @@ -472,7 +445,7 @@ nsec3_iteration_count_high(struct val_env* ve, struct nsec3_filter* filter, } /** 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; @@ -545,6 +518,7 @@ nsec3_calc_hash(struct region* region, ldns_buffer* buf, (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); @@ -573,22 +547,7 @@ nsec3_calc_b32(struct region* region, ldns_buffer* buf, 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) diff --git a/validator/val_nsec3.h b/validator/val_nsec3.h index af83e6f0f..1c55d4ff3 100644 --- a/validator/val_nsec3.h +++ b/validator/val_nsec3.h @@ -66,7 +66,9 @@ #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; @@ -211,4 +213,60 @@ nsec3_prove_nxornodata(struct module_env* env, struct val_env* ve, 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 */