diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index 21b0bea3f93f0776e1285f01479ce720d82546b7..1eea00aace1031fa2c7cfe847849e139c44e46b6 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -293,7 +293,8 @@ static int process_authority(knot_pkt_t *pkt, struct kr_request *req)
 			if (knot_dname_is_sub(rr->owner, qry->zone_cut.name)) {
 				qry->zone_cut.name = knot_dname_copy(rr->owner, &req->pool);
 				if (qry->flags & QUERY_DNSSEC_WANT) { /* Treat as a referral */
-					return KNOT_STATE_DONE;
+					result = KNOT_STATE_DONE;
+					break;
 				}
 			}
 		}
diff --git a/lib/layer/validate.c b/lib/layer/validate.c
index cdd05b8878d3f917d22280b735db76d1035905bd..0d700bcd6e5ccabab0861d33aa98cc1cded074cf 100644
--- a/lib/layer/validate.c
+++ b/lib/layer/validate.c
@@ -138,6 +138,7 @@ static int validate_records(struct kr_query *qry, knot_pkt_t *answer, mm_ctx_t *
 static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_nsec3)
 {
 	/* Merge DNSKEY records from answer that are below/at current cut. */
+	bool updated_key = false;
 	const knot_pktsection_t *an = knot_pkt_section(answer, KNOT_ANSWER);
 	for (unsigned i = 0; i < an->count; ++i) {
 		const knot_rrset_t *rr = knot_pkt_rr(an, i);
@@ -150,6 +151,7 @@ static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_ns
 			if (!qry->zone_cut.key) {
 				return kr_error(ENOMEM);
 			}
+			updated_key = true;
 		} else {
 			int ret = knot_rdataset_merge(&qry->zone_cut.key->rrs,
 			                              &rr->rrs, qry->zone_cut.pool);
@@ -157,6 +159,7 @@ static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_ns
 				knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool);
 				return ret;
 			}
+			updated_key = true;
 		}
 	}
 	if (!qry->zone_cut.key) {
@@ -164,7 +167,7 @@ static int validate_keyset(struct kr_query *qry, knot_pkt_t *answer, bool has_ns
 	}
 
 	/* Check if there's a key for current TA. */
-	if (!(qry->flags & QUERY_CACHED)) {
+	if (updated_key && !(qry->flags & QUERY_CACHED)) {
 		int ret = kr_dnskeys_trusted(answer, KNOT_ANSWER, qry->zone_cut.key,
 		                             qry->zone_cut.trust_anchor, qry->zone_cut.name,
 		                             qry->timestamp.tv_sec, has_nsec3);
@@ -335,40 +338,46 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
 		return KNOT_STATE_FAIL;
 	}
 
-	/* Check if this is a DNSKEY answer, check trust chain and store. */
-	uint16_t qtype = knot_pkt_qtype(pkt);
-	bool has_nsec3 = _knot_pkt_has_type(pkt, KNOT_RRTYPE_NSEC3);
-	if (qtype == KNOT_RRTYPE_DNSKEY) {
-		ret = validate_keyset(qry, pkt, has_nsec3);
-		if (ret != 0) {
-			DEBUG_MSG(qry, "<= bad keys, broken trust chain\n");
-			qry->flags |= QUERY_DNSSEC_BOGUS;
-			return KNOT_STATE_FAIL;
-		}
-	}
-
-	/* Check whether the current zone cut holds keys that can be used
-	 * for validation (i.e. RRSIG signer name matches key owner).
+	/* Check if we descended to a NS, which is authoritative for both parent-child, this means that
+	 * the signatures are made using a different key than we have.
+	 * Although we can't "pause" the response procesing and fetch the keys, we can
+	 * say "do not cache this answer, and try again". This way, the resolver will realise
+	 * that the keys are missing and will schedule a subrequest before retrying.
 	 */
-	const knot_dname_t *key_own = qry->zone_cut.key ? qry->zone_cut.key->owner : NULL;
+	const knot_dname_t *key_own = qry->zone_cut.trust_anchor ? qry->zone_cut.trust_anchor->owner : NULL;
 	const knot_dname_t *sig_name = first_rrsig_signer_name(pkt);
-	if (use_signatures && key_own && sig_name && !knot_dname_is_equal(key_own, sig_name)) {
+	if (use_signatures && sig_name && key_own && !knot_dname_is_equal(key_own, sig_name)) {
 		DEBUG_MSG(qry, ">< cut changed, needs revalidation\n");
 		if (knot_dname_is_sub(sig_name, qry->zone_cut.name)) {
 			qry->zone_cut.name = knot_dname_copy(sig_name, &req->pool);
-		} else if (!knot_dname_is_equal(sig_name, qry->zone_cut.name) && qry->zone_cut.parent) {
+		} else if (!knot_dname_is_equal(sig_name, qry->zone_cut.name)) {
 			/* Key signer is above the current cut, so we can't validate it. This happens when
 			   a server is authoritative for both grandparent, parent and child zone.
 			   Ascend to parent cut, and refetch authority for signer. */
-			memcpy(&qry->zone_cut, qry->zone_cut.parent, sizeof(qry->zone_cut));
+			if (qry->zone_cut.parent) {
+				memcpy(&qry->zone_cut, qry->zone_cut.parent, sizeof(qry->zone_cut));
+			} else {
+				qry->flags |= QUERY_AWAIT_CUT;
+			}
 			qry->zone_cut.name = knot_dname_copy(sig_name, &req->pool);
-		}
+		} /* else zone cut matches, but DS/DNSKEY doesn't => refetch. */
 		knot_wire_set_rcode(pkt->wire, KNOT_RCODE_SERVFAIL); /* Prevent caching */
 		qry->flags &= ~QUERY_RESOLVED;
 		return KNOT_STATE_CONSUME;
 	}
 
+	/* Check if this is a DNSKEY answer, check trust chain and store. */
 	uint8_t pkt_rcode = knot_wire_get_rcode(pkt->wire);
+	uint16_t qtype = knot_pkt_qtype(pkt);
+	bool has_nsec3 = _knot_pkt_has_type(pkt, KNOT_RRTYPE_NSEC3);
+	if (knot_wire_get_aa(pkt->wire) && qtype == KNOT_RRTYPE_DNSKEY) {
+		ret = validate_keyset(qry, pkt, has_nsec3);
+		if (ret != 0) {
+			DEBUG_MSG(qry, "<= bad keys, broken trust chain\n");
+			qry->flags |= QUERY_DNSSEC_BOGUS;
+			return KNOT_STATE_FAIL;
+		}
+	}
 
 	/* Validate non-existence proof if not positive answer. */
 	if (!(qry->flags & QUERY_CACHED) && pkt_rcode == KNOT_RCODE_NXDOMAIN) {
@@ -389,12 +398,8 @@ static int validate(knot_layer_t *ctx, knot_pkt_t *pkt)
 	 * doesn't have to worry about NSEC/NSEC3
 	 * @todo rework this */
 	if (!(qry->flags & QUERY_CACHED)) {
-		const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_ANSWER);
-		uint16_t answer_count = sec ? sec->count : 0;
-
-		/* Validate no data response. */
-		if ((pkt_rcode == KNOT_RCODE_NOERROR) && (!answer_count) &&
-		    (KNOT_WIRE_AA_MASK & knot_wire_get_flags1(pkt->wire))) {
+		const knot_pktsection_t *an = knot_pkt_section(pkt, KNOT_ANSWER);
+		if (pkt_rcode == KNOT_RCODE_NOERROR && an->count == 0 && knot_wire_get_aa(pkt->wire)) { 
 			/* @todo
 			 * ? quick mechanism to determine which check to preform first
 			 * ? merge the functionality together to share code/resources