]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
zonemd, zonemd unit test in own file.
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 21 Oct 2020 10:04:53 +0000 (12:04 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Wed, 21 Oct 2020 10:04:53 +0000 (12:04 +0200)
Makefile.in
testcode/unitmain.c
testcode/unitmain.h
testcode/unitzonemd.c [new file with mode: 0644]

index d2600e71f0cfe1597aab19e5cd9272db050c7820..690a757ab6c1bdd0d6da86d3cbf4d90bb9ca241c 100644 (file)
@@ -173,10 +173,10 @@ UNITTEST_SRC=testcode/unitanchor.c testcode/unitdname.c \
 testcode/unitlruhash.c testcode/unitmain.c testcode/unitmsgparse.c \
 testcode/unitneg.c testcode/unitregional.c testcode/unitslabhash.c \
 testcode/unitverify.c testcode/readhex.c testcode/testpkts.c testcode/unitldns.c \
-testcode/unitecs.c testcode/unitauth.c
+testcode/unitecs.c testcode/unitauth.c testcode/unitzonemd.c
 UNITTEST_OBJ=unitanchor.lo unitdname.lo unitlruhash.lo unitmain.lo \
 unitmsgparse.lo unitneg.lo unitregional.lo unitslabhash.lo unitverify.lo \
-readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.lo
+readhex.lo testpkts.lo unitldns.lo unitecs.lo unitauth.lo unitzonemd.lo
 UNITTEST_OBJ_LINK=$(UNITTEST_OBJ) worker_cb.lo $(COMMON_OBJ) $(SLDNS_OBJ) \
 $(COMPAT_OBJ)
 DAEMON_SRC=daemon/acl_list.c daemon/cachedump.c daemon/daemon.c \
@@ -1217,6 +1217,7 @@ unitldns.lo unitldns.o: $(srcdir)/testcode/unitldns.c config.h $(srcdir)/util/lo
  $(srcdir)/sldns/sbuffer.h $(srcdir)/sldns/str2wire.h $(srcdir)/sldns/rrdef.h $(srcdir)/sldns/wire2str.h \
  $(srcdir)/sldns/parseutil.h
 unitecs.lo unitecs.o: $(srcdir)/testcode/unitecs.c config.h
+unitzonemd.lo unitzonemd.o: $(srcdir)/testcode/unitzonemd.c config.h $(srcdir)/services/authzone.h
 unitauth.lo unitauth.o: $(srcdir)/testcode/unitauth.c config.h $(srcdir)/services/authzone.h \
  $(srcdir)/util/rbtree.h $(srcdir)/util/locks.h $(srcdir)/util/log.h $(srcdir)/services/mesh.h $(srcdir)/util/netevent.h \
  $(srcdir)/dnscrypt/dnscrypt.h  $(srcdir)/util/data/msgparse.h \
index ba2d33fc4a174e746f8cf03bc150e536eba0147b..6a099b15e5aba7905bc985b52fed70e1dd165695 100644 (file)
@@ -839,380 +839,6 @@ static void respip_test(void)
        respip_conf_actions_test();
 }
 
-#include <ctype.h>
-#include "sldns/str2wire.h"
-#include "services/authzone.h"
-#include "util/data/dname.h"
-#include "util/regional.h"
-#include "validator/val_anchor.h"
-/** Add zone from file for testing */
-struct auth_zone* authtest_addzone(struct auth_zones* az, const char* name,
-       char* fname);
-
-/** zonemd unit test, generate a zonemd digest and check if correct */
-static void zonemd_generate_test(const char* zname, char* zfile,
-       int scheme, int hashalgo, const char* digest)
-{
-       uint8_t zonemd_hash[512];
-       size_t hashlen = 0;
-       char output[1024+1];
-       size_t i;
-       struct auth_zones* az;
-       struct auth_zone* z;
-       int result;
-       struct regional* region = NULL;
-       struct sldns_buffer* buf = NULL;
-       char* reason = NULL;
-       char* digestdup;
-
-       if(!zonemd_hashalgo_supported(hashalgo))
-               return; /* cannot test unsupported algo */
-
-       /* setup environment */
-       az = auth_zones_create();
-       unit_assert(az);
-       region = regional_create();
-       unit_assert(region);
-       buf = sldns_buffer_new(65535);
-       unit_assert(buf);
-
-       /* read file */
-       z = authtest_addzone(az, zname, zfile);
-       unit_assert(z);
-
-       /* create zonemd digest */
-       result = auth_zone_generate_zonemd_hash(z, scheme, hashalgo,
-               zonemd_hash, sizeof(zonemd_hash), &hashlen, region, buf,
-               &reason);
-       if(reason) printf("zonemd failure reason: %s\n", reason);
-       unit_assert(result);
-
-       /* check digest */
-       unit_assert(hashlen*2+1 <= sizeof(output));
-       for(i=0; i<hashlen; i++) {
-               const char* hexl = "0123456789ABCDEF";
-               output[i*2] = hexl[(zonemd_hash[i]&0xf0)>>4];
-               output[i*2+1] = hexl[zonemd_hash[i]&0xf];
-       }
-       output[hashlen*2] = 0;
-       digestdup = strdup(digest);
-       unit_assert(digestdup);
-       for(i=0; i<strlen(digestdup); i++) {
-               digestdup[i] = toupper(digestdup[i]);
-       }
-       if(verbosity >= VERB_ALGO) {
-               char zname[255+1];
-               dname_str(z->name, zname);
-               printf("zonemd generated for %s in %s with "
-                       "scheme=%d hashalgo=%d\n", zname, z->zonefile,
-                       scheme, hashalgo);
-               printf("digest %s\n", output);
-               printf("wanted %s\n", digestdup);
-       }
-       unit_assert(strcmp(output, digestdup) == 0);
-
-       /* delete environment */
-       free(digestdup);
-       auth_zones_delete(az);
-       regional_destroy(region);
-       sldns_buffer_free(buf);
-
-       if(verbosity >= VERB_ALGO) {
-               printf("\n");
-       }
-}
-
-/** loop over files and test generated zonemd digest */
-static void zonemd_generate_tests(void)
-{
-       unit_show_func("services/authzone.c", "auth_zone_generate_zonemd_hash");
-       zonemd_generate_test("example.org", "testdata/zonemd.example1.zone",
-               1, 2, "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D481B7");
-
-       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
-        * from section A.1 */
-       zonemd_generate_test("example", "testdata/zonemd.example_a1.zone",
-               1, 1, "c68090d90a7aed716bc459f9340e3d7c1370d4d24b7e2fc3a1ddc0b9a87153b9a9713b3c9ae5cc27777f98b8e730044c");
-
-       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
-        * from section A.2 */
-       zonemd_generate_test("example", "testdata/zonemd.example_a2.zone",
-               1, 1, "31cefb03814f5062ad12fa951ba0ef5f8da6ae354a415767246f7dc932ceb1e742a2108f529db6a33a11c01493de358d");
-
-       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
-        * from section A.3 SHA384 digest */
-       zonemd_generate_test("example", "testdata/zonemd.example_a3.zone",
-               1, 1, "62e6cf51b02e54b9b5f967d547ce43136792901f9f88e637493daaf401c92c279dd10f0edb1c56f8080211f8480ee306");
-
-       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
-        * from section A.3 SHA512 digest*/
-       zonemd_generate_test("example", "testdata/zonemd.example_a3.zone",
-               1, 2, "08cfa1115c7b948c4163a901270395ea226a930cd2cbcf2fa9a5e6eb85f37c8a4e114d884e66f176eab121cb02db7d652e0cc4827e7a3204f166b47e5613fd27");
-
-       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
-        * from section A.4 */
-       zonemd_generate_test("uri.arpa", "testdata/zonemd.example_a4.zone",
-               1, 1, "1291b78ddf7669b1a39d014d87626b709b55774c5d7d58fadc556439889a10eaf6f11d615900a4f996bd46279514e473");
-
-       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
-        * from section A.5 */
-       zonemd_generate_test("root-servers.net", "testdata/zonemd.example_a5.zone",
-               1, 1, "f1ca0ccd91bd5573d9f431c00ee0101b2545c97602be0a978a3b11dbfc1c776d5b3e86ae3d973d6b5349ba7f04340f79");
-}
-
-/** test the zonemd check routine */
-static void zonemd_check_test(void)
-{
-       const char* zname = "example.org";
-       char* zfile = "testdata/zonemd.example1.zone";
-       int scheme = 1;
-       int hashalgo = 2;
-       const char* digest = "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D481B7";
-       const char* digestwrong = "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D48100";
-       uint8_t hash[512], hashwrong[512];
-       size_t hashlen = 0, hashwronglen = 0;
-       struct auth_zones* az;
-       struct auth_zone* z;
-       int result;
-       struct regional* region = NULL;
-       struct sldns_buffer* buf = NULL;
-       char* reason = NULL;
-
-       if(!zonemd_hashalgo_supported(hashalgo))
-               return; /* cannot test unsupported algo */
-       unit_show_func("services/authzone.c", "auth_zone_generate_zonemd_check");
-
-       /* setup environment */
-       az = auth_zones_create();
-       unit_assert(az);
-       region = regional_create();
-       unit_assert(region);
-       buf = sldns_buffer_new(65535);
-       unit_assert(buf);
-
-       /* read file */
-       z = authtest_addzone(az, zname, zfile);
-       unit_assert(z);
-       hashlen = sizeof(hash);
-       if(sldns_str2wire_hex_buf(digest, hash, &hashlen) != 0) {
-               unit_assert(0); /* parse failure */
-       }
-       hashwronglen = sizeof(hashwrong);
-       if(sldns_str2wire_hex_buf(digestwrong, hashwrong, &hashwronglen) != 0) {
-               unit_assert(0); /* parse failure */
-       }
-
-       /* check return values of the check routine */
-       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
-               hash, hashlen, region, buf, &reason);
-       unit_assert(result && reason == NULL);
-       result = auth_zone_generate_zonemd_check(z, 241, hashalgo,
-               hash, hashlen, region, buf, &reason);
-       unit_assert(!result && strcmp(reason, "unsupported scheme")==0);
-       result = auth_zone_generate_zonemd_check(z, scheme, 242,
-               hash, hashlen, region, buf, &reason);
-       unit_assert(!result && strcmp(reason, "unsupported algorithm")==0);
-       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
-               hash, 2, region, buf, &reason);
-       unit_assert(!result && strcmp(reason, "digest length too small, less than 12")==0);
-       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
-               hashwrong, hashwronglen, region, buf, &reason);
-       unit_assert(!result && strcmp(reason, "incorrect digest")==0);
-       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
-               hashwrong, hashwronglen-3, region, buf, &reason);
-       unit_assert(!result && strcmp(reason, "incorrect digest length")==0);
-
-       /* delete environment */
-       auth_zones_delete(az);
-       regional_destroy(region);
-       sldns_buffer_free(buf);
-
-       if(verbosity >= VERB_ALGO) {
-               printf("\n");
-       }
-}
-
-/** zonemd test verify */
-static void zonemd_verify_test(char* zname, char* zfile, char* tastr,
-       char* date_override, char* result_wanted)
-{
-       time_t now = 0;
-       struct module_stack mods;
-       struct module_env env;
-       char* result = NULL;
-       struct auth_zone* z;
-
-       /* setup test harness */
-       memset(&mods, 0, sizeof(mods));
-       memset(&env, 0, sizeof(env));
-       env.scratch = regional_create();
-       if(!env.scratch)
-               fatal_exit("out of memory");
-       env.scratch_buffer = sldns_buffer_new(65553);
-       if(!env.scratch_buffer)
-               fatal_exit("out of memory");
-       env.cfg = config_create();
-       if(!env.cfg)
-               fatal_exit("out of memory");
-       env.now = &now;
-       env.cfg->val_date_override = cfg_convert_timeval(date_override);
-       if(!env.cfg->val_date_override)
-               fatal_exit("could not parse datetime %s", date_override);
-       if(env.cfg->module_conf)
-               free(env.cfg->module_conf);
-       env.cfg->module_conf = strdup("validator iterator");
-       if(!env.cfg->module_conf)
-               fatal_exit("out of memory");
-       if(tastr) {
-               if(!cfg_strlist_insert(&env.cfg->trust_anchor_list,
-                       strdup(tastr)))
-                       fatal_exit("out of memory");
-       }
-       env.anchors = anchors_create();
-       if(!env.anchors)
-               fatal_exit("out of memory");
-       env.auth_zones = auth_zones_create();
-       if(!env.auth_zones)
-               fatal_exit("out of memory");
-       modstack_init(&mods);
-       if(!modstack_setup(&mods, env.cfg->module_conf, &env))
-               fatal_exit("could not modstack_setup");
-       env.mesh = mesh_create(&mods, &env);
-       if(!env.mesh)
-               fatal_exit("out of memory");
-
-       /* load data */
-       z = authtest_addzone(env.auth_zones, zname, zfile);
-       if(!z)
-               fatal_exit("could not addzone %s %s", zname, zfile);
-
-       /* test */
-       lock_rw_wrlock(&z->lock);
-       auth_zone_verify_zonemd(z, &env, &result);
-       lock_rw_unlock(&z->lock);
-       if(verbosity >= VERB_ALGO) {
-               printf("auth zone %s: ZONEMD verification %s: %s\n", zname,
-                       (strcmp(result, "ZONEMD verification successful")==0?"successful":"failed"),
-                       result);
-       }
-       if(!result)
-               fatal_exit("out of memory");
-       unit_assert(strcmp(result, result_wanted) == 0);
-       if(strcmp(result, "ZONEMD verification successful") == 0 ||
-               strcmp(result, "DNSSEC verified nonexistence of ZONEMD") == 0 ||
-               strcmp(result, "no ZONEMD present") == 0) {
-               lock_rw_rdlock(&z->lock);
-               unit_assert(!z->zone_expired);
-               lock_rw_unlock(&z->lock);
-       } else {
-               lock_rw_rdlock(&z->lock);
-               unit_assert(z->zone_expired);
-               lock_rw_unlock(&z->lock);
-       }
-       free(result);
-
-       /* desetup test harness */
-       mesh_delete(env.mesh);
-       modstack_desetup(&mods, &env);
-       auth_zones_delete(env.auth_zones);
-       anchors_delete(env.anchors);
-       config_delete(env.cfg);
-       regional_destroy(env.scratch);
-       sldns_buffer_free(env.scratch_buffer);
-
-       if(verbosity >= VERB_ALGO) {
-               printf("\n");
-       }
-}
-
-/** zonemd test verify suite */
-static void zonemd_verify_tests(void)
-{
-       unit_show_func("services/authzone.c", "auth_zone_verify_zonemd");
-       zonemd_verify_test("example.org",
-               "testdata/zonemd.example1.zone",
-               "example.org. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
-               "20180302005009",
-               "have trust anchor, but zone has no DNSKEY");
-       zonemd_verify_test("example.org",
-               "testdata/zonemd.example1.zone",
-               NULL,
-               "20180302005009",
-               "no ZONEMD present");
-       /* no trust anchor, so it succeeds */
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example2.zone",
-               NULL,
-               "20180302005009",
-               "ZONEMD verification successful");
-       /* trust anchor for another zone, so it is indeterminate */
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example2.zone",
-               "example.org. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
-               "20180302005009",
-               "ZONEMD verification successful");
-
-       /* load a DNSSEC signed zone, but no trust anchor */
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example3.zone",
-               NULL,
-               "20180302005009",
-               "incorrect digest");
-       /* load a DNSSEC zone with NSEC3, but no trust anchor */
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example4.zone",
-               NULL,
-               "20180302005009",
-               "incorrect digest");
-       /* valid zonemd, in dnssec signed zone, no trust anchor*/
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example5.zone",
-               NULL,
-               "20180302005009",
-               "ZONEMD verification successful");
-       /* valid zonemd, in dnssec NSEC3 zone, no trust anchor*/
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example6.zone",
-               NULL,
-               "20180302005009",
-               "ZONEMD verification successful");
-
-       /* load a DNSSEC signed zone with a trust anchor, valid ZONEMD */
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example5.zone",
-               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
-               "20201020135527",
-               "ZONEMD verification successful");
-       /* load a DNSSEC NSEC3 signed zone with a trust anchor, valid ZONEMD */
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example6.zone",
-               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
-               "20201020135527",
-               "ZONEMD verification successful");
-
-       /* load a DNSSEC NSEC zone without ZONEMD */
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example7.zone",
-               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
-               "20201020135527",
-               "DNSSEC verified nonexistence of ZONEMD");
-       /* load a DNSSEC NSEC3 zone without ZONEMD */
-       zonemd_verify_test("example.com",
-               "testdata/zonemd.example8.zone",
-               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
-               "20201020135527",
-               "DNSSEC verified nonexistence of ZONEMD");
-}
-
-/** zonemd unit tests */
-static void zonemd_test(void)
-{
-       unit_show_feature("zonemd");
-       zonemd_generate_tests();
-       zonemd_check_test();
-       zonemd_verify_tests();
-}
-
 void unit_show_func(const char* file, const char* func)
 {
        printf("test %s:%s\n", file, func);
index e5c6109a2aae019043f8b3f23cebb097b6c71f52..66d1322f2a16df56559951a3452549be2301f5ae 100644 (file)
@@ -80,5 +80,7 @@ void ecs_test(void);
 void ldns_test(void);
 /** unit test for auth zone functions */
 void authzone_test(void);
+/** unit test for zonemd functions */
+void zonemd_test(void);
 
 #endif /* TESTCODE_UNITMAIN_H */
diff --git a/testcode/unitzonemd.c b/testcode/unitzonemd.c
new file mode 100644 (file)
index 0000000..8ba8fed
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * testcode/unitzonemd.c - unit test for zonemd.
+ *
+ * Copyright (c) 2020, NLnet Labs. All rights reserved.
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+/**
+ * \file
+ * Unit tests for ZONEMD functionality.
+ */
+
+#include "config.h"
+#include <ctype.h>
+#include "util/log.h"
+#include "testcode/unitmain.h"
+#include "sldns/str2wire.h"
+#include "services/authzone.h"
+#include "util/data/dname.h"
+#include "util/regional.h"
+#include "validator/val_anchor.h"
+
+/** Add zone from file for testing */
+struct auth_zone* authtest_addzone(struct auth_zones* az, const char* name,
+       char* fname);
+
+/** zonemd unit test, generate a zonemd digest and check if correct */
+static void zonemd_generate_test(const char* zname, char* zfile,
+       int scheme, int hashalgo, const char* digest)
+{
+       uint8_t zonemd_hash[512];
+       size_t hashlen = 0;
+       char output[1024+1];
+       size_t i;
+       struct auth_zones* az;
+       struct auth_zone* z;
+       int result;
+       struct regional* region = NULL;
+       struct sldns_buffer* buf = NULL;
+       char* reason = NULL;
+       char* digestdup;
+
+       if(!zonemd_hashalgo_supported(hashalgo))
+               return; /* cannot test unsupported algo */
+
+       /* setup environment */
+       az = auth_zones_create();
+       unit_assert(az);
+       region = regional_create();
+       unit_assert(region);
+       buf = sldns_buffer_new(65535);
+       unit_assert(buf);
+
+       /* read file */
+       z = authtest_addzone(az, zname, zfile);
+       unit_assert(z);
+
+       /* create zonemd digest */
+       result = auth_zone_generate_zonemd_hash(z, scheme, hashalgo,
+               zonemd_hash, sizeof(zonemd_hash), &hashlen, region, buf,
+               &reason);
+       if(reason) printf("zonemd failure reason: %s\n", reason);
+       unit_assert(result);
+
+       /* check digest */
+       unit_assert(hashlen*2+1 <= sizeof(output));
+       for(i=0; i<hashlen; i++) {
+               const char* hexl = "0123456789ABCDEF";
+               output[i*2] = hexl[(zonemd_hash[i]&0xf0)>>4];
+               output[i*2+1] = hexl[zonemd_hash[i]&0xf];
+       }
+       output[hashlen*2] = 0;
+       digestdup = strdup(digest);
+       unit_assert(digestdup);
+       for(i=0; i<strlen(digestdup); i++) {
+               digestdup[i] = toupper(digestdup[i]);
+       }
+       if(verbosity >= VERB_ALGO) {
+               char zname[255+1];
+               dname_str(z->name, zname);
+               printf("zonemd generated for %s in %s with "
+                       "scheme=%d hashalgo=%d\n", zname, z->zonefile,
+                       scheme, hashalgo);
+               printf("digest %s\n", output);
+               printf("wanted %s\n", digestdup);
+       }
+       unit_assert(strcmp(output, digestdup) == 0);
+
+       /* delete environment */
+       free(digestdup);
+       auth_zones_delete(az);
+       regional_destroy(region);
+       sldns_buffer_free(buf);
+
+       if(verbosity >= VERB_ALGO) {
+               printf("\n");
+       }
+}
+
+/** loop over files and test generated zonemd digest */
+static void zonemd_generate_tests(void)
+{
+       unit_show_func("services/authzone.c", "auth_zone_generate_zonemd_hash");
+       zonemd_generate_test("example.org", "testdata/zonemd.example1.zone",
+               1, 2, "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D481B7");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.1 */
+       zonemd_generate_test("example", "testdata/zonemd.example_a1.zone",
+               1, 1, "c68090d90a7aed716bc459f9340e3d7c1370d4d24b7e2fc3a1ddc0b9a87153b9a9713b3c9ae5cc27777f98b8e730044c");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.2 */
+       zonemd_generate_test("example", "testdata/zonemd.example_a2.zone",
+               1, 1, "31cefb03814f5062ad12fa951ba0ef5f8da6ae354a415767246f7dc932ceb1e742a2108f529db6a33a11c01493de358d");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.3 SHA384 digest */
+       zonemd_generate_test("example", "testdata/zonemd.example_a3.zone",
+               1, 1, "62e6cf51b02e54b9b5f967d547ce43136792901f9f88e637493daaf401c92c279dd10f0edb1c56f8080211f8480ee306");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.3 SHA512 digest*/
+       zonemd_generate_test("example", "testdata/zonemd.example_a3.zone",
+               1, 2, "08cfa1115c7b948c4163a901270395ea226a930cd2cbcf2fa9a5e6eb85f37c8a4e114d884e66f176eab121cb02db7d652e0cc4827e7a3204f166b47e5613fd27");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.4 */
+       zonemd_generate_test("uri.arpa", "testdata/zonemd.example_a4.zone",
+               1, 1, "1291b78ddf7669b1a39d014d87626b709b55774c5d7d58fadc556439889a10eaf6f11d615900a4f996bd46279514e473");
+
+       /* https://tools.ietf.org/html/draft-ietf-dnsop-dns-zone-digest-12
+        * from section A.5 */
+       zonemd_generate_test("root-servers.net", "testdata/zonemd.example_a5.zone",
+               1, 1, "f1ca0ccd91bd5573d9f431c00ee0101b2545c97602be0a978a3b11dbfc1c776d5b3e86ae3d973d6b5349ba7f04340f79");
+}
+
+/** test the zonemd check routine */
+static void zonemd_check_test(void)
+{
+       const char* zname = "example.org";
+       char* zfile = "testdata/zonemd.example1.zone";
+       int scheme = 1;
+       int hashalgo = 2;
+       const char* digest = "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D481B7";
+       const char* digestwrong = "20564D10F50A0CEBEC856C64032B7DFB53D3C449A421A5BC7A21F7627B4ACEA4DF29F2C6FE82ED9C23ADF6F4D420D5DD63EF6E6349D60FDAB910B65DF8D48100";
+       uint8_t hash[512], hashwrong[512];
+       size_t hashlen = 0, hashwronglen = 0;
+       struct auth_zones* az;
+       struct auth_zone* z;
+       int result;
+       struct regional* region = NULL;
+       struct sldns_buffer* buf = NULL;
+       char* reason = NULL;
+
+       if(!zonemd_hashalgo_supported(hashalgo))
+               return; /* cannot test unsupported algo */
+       unit_show_func("services/authzone.c", "auth_zone_generate_zonemd_check");
+
+       /* setup environment */
+       az = auth_zones_create();
+       unit_assert(az);
+       region = regional_create();
+       unit_assert(region);
+       buf = sldns_buffer_new(65535);
+       unit_assert(buf);
+
+       /* read file */
+       z = authtest_addzone(az, zname, zfile);
+       unit_assert(z);
+       hashlen = sizeof(hash);
+       if(sldns_str2wire_hex_buf(digest, hash, &hashlen) != 0) {
+               unit_assert(0); /* parse failure */
+       }
+       hashwronglen = sizeof(hashwrong);
+       if(sldns_str2wire_hex_buf(digestwrong, hashwrong, &hashwronglen) != 0) {
+               unit_assert(0); /* parse failure */
+       }
+
+       /* check return values of the check routine */
+       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
+               hash, hashlen, region, buf, &reason);
+       unit_assert(result && reason == NULL);
+       result = auth_zone_generate_zonemd_check(z, 241, hashalgo,
+               hash, hashlen, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "unsupported scheme")==0);
+       result = auth_zone_generate_zonemd_check(z, scheme, 242,
+               hash, hashlen, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "unsupported algorithm")==0);
+       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
+               hash, 2, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "digest length too small, less than 12")==0);
+       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
+               hashwrong, hashwronglen, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "incorrect digest")==0);
+       result = auth_zone_generate_zonemd_check(z, scheme, hashalgo,
+               hashwrong, hashwronglen-3, region, buf, &reason);
+       unit_assert(!result && strcmp(reason, "incorrect digest length")==0);
+
+       /* delete environment */
+       auth_zones_delete(az);
+       regional_destroy(region);
+       sldns_buffer_free(buf);
+
+       if(verbosity >= VERB_ALGO) {
+               printf("\n");
+       }
+}
+
+/** zonemd test verify */
+static void zonemd_verify_test(char* zname, char* zfile, char* tastr,
+       char* date_override, char* result_wanted)
+{
+       time_t now = 0;
+       struct module_stack mods;
+       struct module_env env;
+       char* result = NULL;
+       struct auth_zone* z;
+
+       /* setup test harness */
+       memset(&mods, 0, sizeof(mods));
+       memset(&env, 0, sizeof(env));
+       env.scratch = regional_create();
+       if(!env.scratch)
+               fatal_exit("out of memory");
+       env.scratch_buffer = sldns_buffer_new(65553);
+       if(!env.scratch_buffer)
+               fatal_exit("out of memory");
+       env.cfg = config_create();
+       if(!env.cfg)
+               fatal_exit("out of memory");
+       env.now = &now;
+       env.cfg->val_date_override = cfg_convert_timeval(date_override);
+       if(!env.cfg->val_date_override)
+               fatal_exit("could not parse datetime %s", date_override);
+       if(env.cfg->module_conf)
+               free(env.cfg->module_conf);
+       env.cfg->module_conf = strdup("validator iterator");
+       if(!env.cfg->module_conf)
+               fatal_exit("out of memory");
+       if(tastr) {
+               if(!cfg_strlist_insert(&env.cfg->trust_anchor_list,
+                       strdup(tastr)))
+                       fatal_exit("out of memory");
+       }
+       env.anchors = anchors_create();
+       if(!env.anchors)
+               fatal_exit("out of memory");
+       env.auth_zones = auth_zones_create();
+       if(!env.auth_zones)
+               fatal_exit("out of memory");
+       modstack_init(&mods);
+       if(!modstack_setup(&mods, env.cfg->module_conf, &env))
+               fatal_exit("could not modstack_setup");
+       env.mesh = mesh_create(&mods, &env);
+       if(!env.mesh)
+               fatal_exit("out of memory");
+
+       /* load data */
+       z = authtest_addzone(env.auth_zones, zname, zfile);
+       if(!z)
+               fatal_exit("could not addzone %s %s", zname, zfile);
+
+       /* test */
+       lock_rw_wrlock(&z->lock);
+       auth_zone_verify_zonemd(z, &env, &result);
+       lock_rw_unlock(&z->lock);
+       if(verbosity >= VERB_ALGO) {
+               printf("auth zone %s: ZONEMD verification %s: %s\n", zname,
+                       (strcmp(result, "ZONEMD verification successful")==0?"successful":"failed"),
+                       result);
+       }
+       if(!result)
+               fatal_exit("out of memory");
+       unit_assert(strcmp(result, result_wanted) == 0);
+       if(strcmp(result, "ZONEMD verification successful") == 0 ||
+               strcmp(result, "DNSSEC verified nonexistence of ZONEMD") == 0 ||
+               strcmp(result, "no ZONEMD present") == 0) {
+               lock_rw_rdlock(&z->lock);
+               unit_assert(!z->zone_expired);
+               lock_rw_unlock(&z->lock);
+       } else {
+               lock_rw_rdlock(&z->lock);
+               unit_assert(z->zone_expired);
+               lock_rw_unlock(&z->lock);
+       }
+       free(result);
+
+       /* desetup test harness */
+       mesh_delete(env.mesh);
+       modstack_desetup(&mods, &env);
+       auth_zones_delete(env.auth_zones);
+       anchors_delete(env.anchors);
+       config_delete(env.cfg);
+       regional_destroy(env.scratch);
+       sldns_buffer_free(env.scratch_buffer);
+
+       if(verbosity >= VERB_ALGO) {
+               printf("\n");
+       }
+}
+
+/** zonemd test verify suite */
+static void zonemd_verify_tests(void)
+{
+       unit_show_func("services/authzone.c", "auth_zone_verify_zonemd");
+       zonemd_verify_test("example.org",
+               "testdata/zonemd.example1.zone",
+               "example.org. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20180302005009",
+               "have trust anchor, but zone has no DNSKEY");
+       zonemd_verify_test("example.org",
+               "testdata/zonemd.example1.zone",
+               NULL,
+               "20180302005009",
+               "no ZONEMD present");
+       /* no trust anchor, so it succeeds */
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example2.zone",
+               NULL,
+               "20180302005009",
+               "ZONEMD verification successful");
+       /* trust anchor for another zone, so it is indeterminate */
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example2.zone",
+               "example.org. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20180302005009",
+               "ZONEMD verification successful");
+
+       /* load a DNSSEC signed zone, but no trust anchor */
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example3.zone",
+               NULL,
+               "20180302005009",
+               "incorrect digest");
+       /* load a DNSSEC zone with NSEC3, but no trust anchor */
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example4.zone",
+               NULL,
+               "20180302005009",
+               "incorrect digest");
+       /* valid zonemd, in dnssec signed zone, no trust anchor*/
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example5.zone",
+               NULL,
+               "20180302005009",
+               "ZONEMD verification successful");
+       /* valid zonemd, in dnssec NSEC3 zone, no trust anchor*/
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example6.zone",
+               NULL,
+               "20180302005009",
+               "ZONEMD verification successful");
+
+       /* load a DNSSEC signed zone with a trust anchor, valid ZONEMD */
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example5.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "ZONEMD verification successful");
+       /* load a DNSSEC NSEC3 signed zone with a trust anchor, valid ZONEMD */
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example6.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "ZONEMD verification successful");
+
+       /* load a DNSSEC NSEC zone without ZONEMD */
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example7.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "DNSSEC verified nonexistence of ZONEMD");
+       /* load a DNSSEC NSEC3 zone without ZONEMD */
+       zonemd_verify_test("example.com",
+               "testdata/zonemd.example8.zone",
+               "example.com. IN DS 55566 8 2 9c148338951ce1c3b5cd3da532f3d90dfcf92595148022f2c2fd98e5deee90af",
+               "20201020135527",
+               "DNSSEC verified nonexistence of ZONEMD");
+}
+
+/** zonemd unit tests */
+void zonemd_test(void)
+{
+       unit_show_feature("zonemd");
+       zonemd_generate_tests();
+       zonemd_check_test();
+       zonemd_verify_tests();
+}