]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
finish up adding validator EDEs and other TODOs and fix tests with more specific...
authorTCY16 <tom@nlnetlabs.nl>
Mon, 6 Dec 2021 13:19:28 +0000 (14:19 +0100)
committerTCY16 <tom@nlnetlabs.nl>
Mon, 6 Dec 2021 13:19:28 +0000 (14:19 +0100)
13 files changed:
daemon/worker.c
services/localzone.c
services/mesh.c
testcode/testpkts.c
testdata/black_ds_entry.rpl
testdata/black_key_entry.rpl
testdata/black_prime_entry.rpl
testdata/val_nsec3_b2_nodata_nons.rpl
testdata/val_secds_nosig.rpl
util/config_file.h
validator/val_sigcrypt.c
validator/val_utils.c
validator/validator.c

index 19463c047c89f6323d69e0b68de80f5e89ac9577..ec028fb380008242a5f5004e41ef654eb4969802 100644 (file)
@@ -1582,12 +1582,12 @@ lookup_cache:
                                                leeway = 0;
                                        lock_rw_unlock(&e->lock);
 
+                                       // @TODO test this
                                        // // stale answer? 
                                        // if (worker->env.cfg->serve_expired &&
                                        //      *worker->env.now >= ((struct reply_info*)e->data)->ttl) {
-                                       //      // EDE Error Code 3 - Stale Answer
-                                       //      EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out, worker->scratchpad,
-                                       // LDNS_EDE_STALE_ANSWER, "");
+                                       //      EDNS_OPT_LIST_APPEND_EDE(&edns.opt_list_out,
+                                       //              worker->scratchpad, LDNS_EDE_STALE_ANSWER, "");
                                        // }
 
                                        // add EDNS struct?
index 835c99538dc8a5205dc0605888689955e57296f1..c8987bbedda1585a210ec643dee46a32975730e6 100644 (file)
@@ -1813,9 +1813,8 @@ local_data_answer(struct local_zone* z, struct module_env* env,
                return local_encode(qinfo, env, edns, repinfo, buf, temp, &r, 1,
                        LDNS_RCODE_NOERROR);
        }
-       // @TODO add EDE?
-       return local_encode(qinfo, env, edns, repinfo, buf, temp, lr->rrset, 1,
-               LDNS_RCODE_NOERROR);
+       return local_encode_ede(z, qinfo, env, edns, repinfo, buf, temp, lr->rrset, 1,
+               LDNS_RCODE_NOERROR, LDNS_EDE_FORGED_ANSWER, "", do_ede);
 }
 
 /**
index fae8bee787b79e269802bb46e944f508530a78f9..60a7e22ff6cb47bfb9c38b76e87477bc0df701dd 100644 (file)
@@ -1329,7 +1329,8 @@ mesh_send_reply(struct mesh_state* m, int rcode, struct reply_info* rep,
                        if(!inplace_cb_reply_servfail_call(m->s.env, &m->s.qinfo, &m->s,
                        rep, LDNS_RCODE_SERVFAIL, &r->edns, &r->query_reply, m->s.region, &r->start_time))
                                r->edns.opt_list_inplace_cb_out = NULL;
-                       // @TODO EDE?
+                       /* internal server error (probably malloc failure) so no
+                        * EDE (RFC8914) needed */
                        error_encode(r_buffer, LDNS_RCODE_SERVFAIL,
                                &m->s.qinfo, r->qid, r->qflags, &r->edns);
                }
index 9ade84591cd58cfdfa258036a774ea1019949949..0c2dddac1e651bb0f880570c5b2fdfab9bb441f7 100644 (file)
@@ -1549,7 +1549,8 @@ find_match(struct entry* entries, uint8_t* query_pkt, size_t len,
                        int info_code = get_ede_info_code(query_pkt, len);
                        if(info_code == -1 ||
                                (uint16_t)info_code != p->ede_info_code) {
-                               verbose(3, "bad EDE INFO-CODE\n");
+                               verbose(3, "bad EDE INFO-CODE. Expected: %d, and got: %d\n",
+                                       p->ede_info_code, info_code);
                                continue;
                        }
                }
index 4a4133cd999bc3f1ea100989104d5e6a253d4f16..d56da140a7dad17d2550aadab2ed5323d6b65fe2 100644 (file)
@@ -578,7 +578,7 @@ ENTRY_END
 ; recursion happens here.
 STEP 10 CHECK_ANSWER
 ENTRY_BEGIN
-MATCH all_noedns ede=6
+MATCH all_noedns ede=7
 REPLY QR RD RA DO SERVFAIL
 SECTION QUESTION
 www.sub.example.com. IN A
index 7be6a2c5b7375f6f3b6e88d9d0cdf74a1d8780bf..e1c5f5e78f35d4bf1ae7e0cecfc6f6661a8aceb0 100644 (file)
@@ -560,7 +560,7 @@ ENTRY_END
 ; recursion happens here.
 STEP 10 CHECK_ANSWER
 ENTRY_BEGIN
-MATCH all_noedns ede=6
+MATCH all_noedns ede=7
 REPLY QR RD RA DO SERVFAIL
 SECTION QUESTION
 www.sub.example.com. IN A
index ee7ed1db38790fa7c0b13adebd3354eb006a7273..993028d1726b334bc5ae8d17cb71976da4a9c222 100644 (file)
@@ -285,7 +285,7 @@ ENTRY_END
 ; recursion happens here.
 STEP 10 CHECK_ANSWER
 ENTRY_BEGIN
-MATCH all_noedns ede=6
+MATCH all_noedns ede=7
 REPLY QR RD RA DO SERVFAIL
 SECTION QUESTION
 www.example.com. IN A
index 442f5e0430d0c75fa41b7bf8457bf1316738a256..97c600d272ebaefae25769e43298d08a9f67bfba 100644 (file)
@@ -127,7 +127,7 @@ ENTRY_END
 ; recursion happens here.
 STEP 10 CHECK_ANSWER
 ENTRY_BEGIN
-MATCH all_noedns ede=6
+MATCH all_noedns ede=12
 REPLY QR RD RA DO SERVFAIL
 SECTION QUESTION
 ns1.example.        IN MX
index fccb7bf54d35aaa9e85621b44a58d98422d4efb9..8c39a845b4d8daa3a523c2c4b1642846c683bf63 100644 (file)
@@ -223,7 +223,7 @@ ENTRY_END
 ; recursion happens here.
 STEP 10 CHECK_ANSWER
 ENTRY_BEGIN
-MATCH all_noedns ede=6
+MATCH all_noedns ede=10
 REPLY QR RD RA DO SERVFAIL
 SECTION QUESTION
 www.sub.example.com. IN A
index 92592aa1ac30075e67c3279255425f83cbe62d0e..c6eb02a09d0cdaf6810de4f54dc0320d4db3cd9e 100644 (file)
@@ -1281,8 +1281,12 @@ void errinf_dname(struct module_qstate* qstate, const char* str,
  *    This string is malloced and has to be freed by caller.
  */
 char* errinf_to_str_bogus(struct module_qstate* qstate);
-
-// @TODO write this
+/**
+ * Check the sldns_ede_code of the qstate.
+ * @param qstate: query state.
+ * @return LDNS_EDE_DNSSEC_BOGUS by default, or another sldns_ede_code 
+ * if this is set.
+ */
 sldns_ede_code errinf_to_reason_bogus(struct module_qstate* qstate);
 
 /**
index 777376c1fd997ff8653568f85d21562cced644e6..4873861976e8f9eefae1c3c3fd4b077772d55b55 100644 (file)
@@ -627,6 +627,15 @@ dnskey_verify_rrset(struct module_env* env, struct val_env* ve,
                reason, NULL, section, qstate);
 }
 
+static enum sec_status
+dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
+       struct val_env* ve, time_t now,
+        struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
+        size_t dnskey_idx, size_t sig_idx,
+       struct rbtree_type** sortree, int* buf_canon,
+       char** reason, sldns_ede_code *reason_bogus,
+       sldns_pkt_section section, struct module_qstate* qstate);
+
 enum sec_status 
 dnskey_verify_rrset_ede(struct module_env* env, struct val_env* ve,
         struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
@@ -655,11 +664,10 @@ dnskey_verify_rrset_ede(struct module_env* env, struct val_env* ve,
                        tag != rrset_get_sig_keytag(rrset, i))
                        continue;
                buf_canon = 0;
-               // @TODO should this be _ede verion as well?
-               sec = dnskey_verify_rrset_sig(env->scratch, 
+               sec = dnskey_verify_rrset_sig_ede(env->scratch,
                        env->scratch_buffer, ve, *env->now, rrset, 
                        dnskey, dnskey_idx, i, &sortree, &buf_canon, reason,
-                       section, qstate);
+                       reason_bogus, section, qstate);
                if(sec == sec_status_secure)
                        return sec;
                numchecked ++;
@@ -669,15 +677,6 @@ dnskey_verify_rrset_ede(struct module_env* env, struct val_env* ve,
        return sec_status_bogus;
 }
 
-static enum sec_status 
-dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf, 
-       struct val_env* ve, time_t now,
-        struct ub_packed_rrset_key* rrset, struct ub_packed_rrset_key* dnskey,
-        size_t dnskey_idx, size_t sig_idx,
-       struct rbtree_type** sortree, int* buf_canon,
-       char** reason, sldns_ede_code *reason_bogus,
-       sldns_pkt_section section, struct module_qstate* qstate);
-
 static enum sec_status 
 dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve, 
        time_t now, struct ub_packed_rrset_key* rrset, 
@@ -695,9 +694,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
        int buf_canon = 0;
        verbose(VERB_ALGO, "verify sig %d %d", (int)tag, algo);
        if(!dnskey_algo_id_is_supported(algo)) {
-               // @TODO do we do LDNS_EDE_UNSUPPORTED_DNSKEY_ALG here?
-               // if (reason_bogus)
-               //      *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
+               if (reason_bogus)
+                       *reason_bogus = LDNS_EDE_UNSUPPORTED_DNSKEY_ALG;
                verbose(VERB_QUERY, "verify sig: unknown algorithm");
                return sec_status_insecure;
        }
@@ -719,6 +717,8 @@ dnskeyset_verify_rrset_sig(struct module_env* env, struct val_env* ve,
        }
        if(numchecked == 0) {
                *reason = "signatures from unknown keys";
+               if (reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSKEY_MISSING;
                verbose(VERB_QUERY, "verify: could not find appropriate key");
                return sec_status_bogus;
        }
@@ -1436,7 +1436,13 @@ check_dates(struct val_env* ve, uint32_t unow, uint8_t* expi_p,
                sigdate_error("verify: inception after expiration, "
                        "signature bad", expi, incep, now);
                *reason = "signature inception after expiration";
-               /* @TODO Tom, wat hier te doen? */
+               if (reason_bogus){
+                       /* from RFC8914 on Signature Not Yet Valid: The resolver
+                        * attempted to perform DNSSEC validation, but no
+                        * signatures are presently valid and at least some are
+                        * not yet valid. */
+                       *reason_bogus = LDNS_EDE_SIGNATURE_NOT_YET_VALID;
+               }
 
                return 0;
        }
@@ -1562,6 +1568,8 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
        if(siglen < 2+20) {
                verbose(VERB_QUERY, "verify: signature too short");
                *reason = "signature too short";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus;
        }
 
@@ -1577,6 +1585,8 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
                /* RFC 4034 says DNSKEY PROTOCOL MUST be 3 */
                verbose(VERB_QUERY, "verify: dnskey has wrong key protocol");
                *reason = "dnskey has wrong protocolnumber";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus;
        }
 
@@ -1586,17 +1596,23 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
        if(!signer_len) {
                verbose(VERB_QUERY, "verify: malformed signer name");
                *reason = "signer name malformed";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus; /* signer name invalid */
        }
        if(!dname_subdomain_c(rrset->rk.dname, signer)) {
                verbose(VERB_QUERY, "verify: signer name is off-tree");
                *reason = "signer name off-tree";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus; /* signer name offtree */
        }
        sigblock = (unsigned char*)signer+signer_len;
        if(siglen < 2+18+signer_len+1) {
                verbose(VERB_QUERY, "verify: too short, no signature data");
                *reason = "signature too short, no signature data";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus; /* sig rdf is < 1 byte */
        }
        sigblock_len = (unsigned int)(siglen - 2 - 18 - signer_len);
@@ -1609,6 +1625,8 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
                log_nametypeclass(VERB_QUERY, "the key name is", 
                        dnskey->rk.dname, 0, 0);
                *reason = "signer name mismatches key name";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus;
        }
 
@@ -1617,18 +1635,24 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
        if(memcmp(sig+2, &rrset->rk.type, 2) != 0) {
                verbose(VERB_QUERY, "verify: wrong type covered");
                *reason = "signature covers wrong type";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus;
        }
        /* verify keytag and sig algo (possibly again) */
        if((int)sig[2+2] != dnskey_get_algo(dnskey, dnskey_idx)) {
                verbose(VERB_QUERY, "verify: wrong algorithm");
                *reason = "signature has wrong algorithm";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus;
        }
        ktag = htons(dnskey_calc_keytag(dnskey, dnskey_idx));
        if(memcmp(sig+2+16, &ktag, 2) != 0) {
                verbose(VERB_QUERY, "verify: wrong keytag");
                *reason = "signature has wrong keytag";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus;
        }
 
@@ -1636,6 +1660,8 @@ dnskey_verify_rrset_sig_ede(struct regional* region, sldns_buffer* buf,
        if((int)sig[2+3] > dname_signame_label_count(rrset->rk.dname)) {
                verbose(VERB_QUERY, "verify: labelcount out of range");
                *reason = "signature labelcount out of range";
+               if(reason_bogus)
+                       *reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                return sec_status_bogus;
        }
 
index 2fa0b37e1a1fd241afe6e8fd2e6d8dc664db141a..b8a8975a42b241b2ac4016bf19635e7c5ee17f30 100644 (file)
@@ -593,8 +593,7 @@ val_verify_DNSKEY_with_DS_ede(struct module_env* env, struct val_env* ve,
        if(!has_useful_ds) {
                verbose(VERB_ALGO, "No usable DS records were found -- "
                        "treating as insecure.");
-               // @TODO add ede DNSSEC Indeterminate?
-               return sec_status_insecure;
+                       return sec_status_insecure;
        }
        /* If any were understandable, then it is bad. */
        verbose(VERB_QUERY, "Failed to match any usable DS to a DNSKEY.");
@@ -850,8 +849,6 @@ val_dsset_isusable(struct ub_packed_rrset_key* ds_rrset)
                else snprintf(aerr, sizeof(aerr), "%d",
                        (int)ds_get_key_algo(ds_rrset, i));
 
-               // @TODO do we want to add EDE Unsupported DS Digest Type here?
-
                verbose(VERB_ALGO, "DS unsupported, hash %s %s, "
                        "key algorithm %s %s", herr,
                        (ds_digest_algo_is_supported(ds_rrset, 0)?
index 5f5804264e7f34b80d21838ab2129b461b48a05b..7bdaaab0d854652494b8e2be99cf6e96985e89fa 100644 (file)
@@ -672,6 +672,7 @@ validate_msg_signatures(struct module_qstate* qstate, struct module_env* env,
                /* only validate rrs that have signatures with the key */
                /* leave others unchecked, those get removed later on too */
                val_find_rrset_signer(s, &sname, &slen);
+
                if(sname && query_dname_compare(sname, key_entry->name)==0)
                        (void)val_verify_rrset_entry(env, ve, s, key_entry,
                                &reason, LDNS_SECTION_ADDITIONAL, qstate);
@@ -1560,6 +1561,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
                        verbose(VERB_QUERY, "unsigned parent zone denies"
                                " trust anchor, indeterminate");
                        vq->chase_reply->security = sec_status_indeterminate;
+                       vq->chase_reply->reason_bogus = LDNS_EDE_DNSSEC_INDETERMINATE;
                        vq->state = VAL_FINISHED_STATE;
                        return 1;
                }
@@ -1591,6 +1593,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
        if(vq->key_entry == NULL && anchor == NULL) {
                /*response isn't under a trust anchor, so we cannot validate.*/
                vq->chase_reply->security = sec_status_indeterminate;
+               vq->chase_reply->reason_bogus = LDNS_EDE_DNSSEC_INDETERMINATE;
                /* go to finished state to cache this result */
                vq->state = VAL_FINISHED_STATE;
                return 1;
@@ -1637,11 +1640,8 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
                return 1;
        } else if(key_entry_isbad(vq->key_entry)) {
                /* key is bad, chain is bad, reply is bogus */
-
-               // @TODO ADD Error Code 6 - DNSSEC Bogus + text chain of trust?
-
                errinf_dname(qstate, "key for validation", vq->key_entry->name);
-               errinf(qstate, "is marked as invalid");
+               errinf_ede(qstate, "is marked as invalid", LDNS_EDE_DNSSEC_BOGUS);
                if(key_entry_get_reason(vq->key_entry)) {
                        errinf(qstate, "because of a previous");
                        errinf(qstate, key_entry_get_reason(vq->key_entry));
@@ -1649,6 +1649,7 @@ processInit(struct module_qstate* qstate, struct val_qstate* vq,
                /* no retries, stop bothering the authority until timeout */
                vq->restart_count = ve->max_restart;
                vq->chase_reply->security = sec_status_bogus;
+               vq->chase_reply->reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
                vq->state = VAL_FINISHED_STATE;
                return 1;
        }
@@ -1719,11 +1720,10 @@ processFindKey(struct module_qstate* qstate, struct val_qstate* vq, int id)
                        vq->empty_DS_name) == 0) {
                        /* do not query for empty_DS_name again */
                        verbose(VERB_ALGO, "Cannot retrieve DS for signature");
-                       errinf(qstate, "no signatures");
-                       vq->chase_reply->reason_bogus = LDNS_EDE_RRSIGS_MISSING;
+                       errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
                        errinf_origin(qstate, qstate->reply_origin);
                        vq->chase_reply->security = sec_status_bogus;
-                       // @TODO add EDE 10 - RRSIGs Missing
+                       vq->chase_reply->reason_bogus = LDNS_EDE_RRSIGS_MISSING;
                        vq->state = VAL_FINISHED_STATE;
                        return 1;
                }
@@ -1856,10 +1856,9 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
                        "of trust to keys for", vq->key_entry->name,
                        LDNS_RR_TYPE_DNSKEY, vq->key_entry->key_class);
                vq->chase_reply->security = sec_status_bogus;
-               errinf(qstate, "while building chain of trust");
-
-               // @TODO ADD Error Code 6 - DNSSEC Bogus + text chain of trust?
-
+               vq->chase_reply->reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
+               errinf_ede(qstate, "while building chain of trust",
+                       LDNS_EDE_DNSSEC_BOGUS);
                if(vq->restart_count >= ve->max_restart)
                        key_cache_insert(ve->kcache, vq->key_entry, qstate);
                return 1;
@@ -1872,10 +1871,10 @@ processValidate(struct module_qstate* qstate, struct val_qstate* vq,
                        "signer name", &vq->qchase);
                verbose(VERB_DETAIL, "Could not establish validation of "
                          "INSECURE status of unsigned response.");
-               errinf(qstate, "no signatures");
-               vq->chase_reply->reason_bogus = LDNS_EDE_RRSIGS_MISSING;
+               errinf_ede(qstate, "no signatures", LDNS_EDE_RRSIGS_MISSING);
                errinf_origin(qstate, qstate->reply_origin);
                vq->chase_reply->security = sec_status_bogus;
+               vq->chase_reply->reason_bogus = LDNS_EDE_RRSIGS_MISSING;
                return 1;
        }
        subtype = val_classify_response(qstate->query_flags, &qstate->qinfo,
@@ -2363,7 +2362,6 @@ primeResponseToKE(struct ub_packed_rrset_key* dnskey_rrset,
                /* NOTE: in this case, we should probably reject the trust 
                 * anchor for longer, perhaps forever. */
                if(qstate->env->cfg->harden_dnssec_stripped) {
-                       // errinf(qstate, reason);
                        errinf_ede(qstate, reason, reason_bogus);
                        kkey = key_entry_create_bad(qstate->region, ta->name,
                                ta->namelen, ta->dclass, BOGUS_KEY_TTL,
@@ -2407,6 +2405,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
 {
        struct val_env* ve = (struct val_env*)qstate->env->modinfo[id];
        char* reason = NULL;
+       sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
        enum val_classification subtype;
        if(rcode != LDNS_RCODE_NOERROR) {
                char rc[16];
@@ -2415,7 +2414,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
                /* errors here pretty much break validation */
                verbose(VERB_DETAIL, "DS response was error, thus bogus");
                errinf(qstate, rc);
-               errinf(qstate, "no DS");
+               errinf_ede(qstate, "no DS", reason_bogus);
 
                goto return_bogus;
        }
@@ -2430,17 +2429,17 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
                if(!ds) {
                        log_warn("internal error: POSITIVE DS response was "
                                "missing DS.");
-                       errinf(qstate, "no DS record");
+                       errinf_ede(qstate, "no DS record", reason_bogus);
                        goto return_bogus;
                }
                /* Verify only returns BOGUS or SECURE. If the rrset is 
                 * bogus, then we are done. */
-               sec = val_verify_rrset_entry(qstate->env, ve, ds, 
-                       vq->key_entry, &reason, LDNS_SECTION_ANSWER, qstate);
+               sec = val_verify_rrset_entry_ede(qstate->env, ve, ds,
+                       vq->key_entry, &reason, &reason_bogus, LDNS_SECTION_ANSWER, qstate);
                if(sec != sec_status_secure) {
                        verbose(VERB_DETAIL, "DS rrset in DS response did "
                                "not verify");
-                       errinf(qstate, reason);
+                       errinf_ede(qstate, reason, reason_bogus);
                        goto return_bogus;
                }
 
@@ -2449,6 +2448,9 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
                if(!val_dsset_isusable(ds)) {
                        /* If they aren't usable, then we treat it like 
                         * there was no DS. */
+
+                       // @TODO add EDE Unsupported DS Digest Type
+
                        *ke = key_entry_create_null(qstate->region, 
                                qinfo->qname, qinfo->qname_len, qinfo->qclass, 
                                ub_packed_rrset_ttl(ds), *qstate->env->now);
@@ -2471,7 +2473,7 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
                /* make sure there are NSECs or NSEC3s with signatures */
                if(!val_has_signed_nsecs(msg->rep, &reason)) {
                        verbose(VERB_ALGO, "no NSECs: %s", reason);
-                       errinf(qstate, reason);
+                       errinf_ede(qstate, reason, LDNS_EDE_NSEC_MISSING);
                        goto return_bogus;
                }
 
@@ -2599,9 +2601,6 @@ ds_response_to_ke(struct module_qstate* qstate, struct val_qstate* vq,
                goto return_bogus;
        }
 return_bogus:
-
-       // @TODO add EDE NSEC MISSING
-
        *ke = key_entry_create_bad(qstate->region, qinfo->qname,
                qinfo->qname_len, qinfo->qclass, 
                BOGUS_KEY_TTL, *qstate->env->now);
@@ -2707,6 +2706,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
        struct ub_packed_rrset_key* dnskey = NULL;
        int downprot;
        char* reason = NULL;
+       sldns_ede_code reason_bogus = LDNS_EDE_DNSSEC_BOGUS;
 
        if(rcode == LDNS_RCODE_NOERROR)
                dnskey = reply_find_answer_rrset(qinfo, msg->rep);
@@ -2716,7 +2716,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
                verbose(VERB_DETAIL, "Missing DNSKEY RRset in response to "
                        "DNSKEY query.");
 
-               // @TODO add EDE 9 - DNSKEY Missing
+               reason_bogus = LDNS_EDE_DNSKEY_MISSING;
 
                if(vq->restart_count < ve->max_restart) {
                        val_blacklist(&vq->chain_blacklist, qstate->region,
@@ -2732,7 +2732,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
                        log_err("alloc failure in missing dnskey response");
                        /* key_entry is NULL for failure in Validate */
                }
-               errinf(qstate, "No DNSKEY record");
+               errinf_ede(qstate, "No DNSKEY record", reason_bogus);
                errinf_origin(qstate, origin);
                errinf_dname(qstate, "for key", qinfo->qname);
                vq->state = VAL_VALIDATE_STATE;
@@ -2745,9 +2745,8 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
                return;
        }
        downprot = qstate->env->cfg->harden_algo_downgrade;
-       // @TODO add _ede
-       vq->key_entry = val_verify_new_DNSKEYs(qstate->region, qstate->env,
-               ve, dnskey, vq->ds_rrset, downprot, &reason, qstate);
+       vq->key_entry = val_verify_new_DNSKEYs_ede(qstate->region, qstate->env,
+               ve, dnskey, vq->ds_rrset, downprot, &reason, &reason_bogus, qstate);
 
        if(!vq->key_entry) {
                log_err("out of memory in verify new DNSKEYs");
@@ -2768,7 +2767,7 @@ process_dnskey_response(struct module_qstate* qstate, struct val_qstate* vq,
                        }
                        verbose(VERB_DETAIL, "Did not match a DS to a DNSKEY, "
                                "thus bogus.");
-                       errinf(qstate, reason);
+                       errinf_ede(qstate, reason, reason_bogus);
                        errinf_origin(qstate, origin);
                        errinf_dname(qstate, "for key", qinfo->qname);
                }