diff options
author | Casey Deccio <casey@deccio.net> | 2021-01-08 00:19:54 +0300 |
---|---|---|
committer | Casey Deccio <casey@deccio.net> | 2021-01-09 03:07:33 +0300 |
commit | 992baacead282d4f927cdc2ac56a2ba0005e8457 (patch) | |
tree | b4f65e805ed64d6f29f502b00811f86d5afce52d | |
parent | 2ce35fefb8d910cfff9eee652e5678bff2ff9c97 (diff) |
Consider algorithms that MUST NOT be validated
See #63 and RFC 8624.
-rw-r--r-- | dnsviz/analysis/errors.py | 36 | ||||
-rw-r--r-- | dnsviz/analysis/status.py | 69 |
2 files changed, 99 insertions, 6 deletions
diff --git a/dnsviz/analysis/errors.py b/dnsviz/analysis/errors.py index c9da104..27a21ce 100644 --- a/dnsviz/analysis/errors.py +++ b/dnsviz/analysis/errors.py @@ -252,6 +252,25 @@ class AlgorithmNotSupported(RRSIGError): super(AlgorithmNotSupported, self).__init__(**kwargs) self.template_kwargs['algorithm_text'] = dns.dnssec.algorithm_to_text(self.template_kwargs['algorithm']) +class AlgorithmMustNotValidate(RRSIGError): + ''' + >>> e = AlgorithmMustNotValidate(algorithm=5) + >>> e.args + [5] + >>> e.description + 'DNSSEC specification prohibits validation of RRSIGs with DNSSEC algorithm 5 (RSASHA1).' + ''' + + _abstract = False + code = 'ALGORITHM_MUST_NOT_VALIDATE' + description_template = "DNSSEC specification prohibits validation of RRSIGs with DNSSEC algorithm %(algorithm)d (%(algorithm_text)s)." + references = ['RFC 8624, Sec. 3.1'] + required_params = ['algorithm'] + + def __init__(self, **kwargs): + super(AlgorithmMustNotValidate, self).__init__(**kwargs) + self.template_kwargs['algorithm_text'] = dns.dnssec.algorithm_to_text(self.template_kwargs['algorithm']) + class DNSKEYRevokedRRSIG(RRSIGError): ''' >>> e = DNSKEYRevokedRRSIG() @@ -514,6 +533,23 @@ class DigestAlgorithmNotSupported(DSDigestError): super(DigestAlgorithmNotSupported, self).__init__(**kwargs) self.template_kwargs['algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['algorithm'], self.template_kwargs['algorithm']) +class DigestAlgorithmMustNotValidate(DSDigestError): + ''' + >>> e = DigestAlgorithmMustNotValidate(algorithm=5) + >>> e.description + 'DNSSEC specification prohibits validation of DS records that use digest algorithm 5 (5).' + ''' + + _abstract = False + code = 'DIGEST_ALGORITHM_MUST_NOT_VALIDATE' + description_template = "DNSSEC specification prohibits validation of DS records that use digest algorithm %(algorithm)d (%(algorithm_text)s)." + references = ['RFC 8624, Sec. 3.2'] + required_params = ['algorithm'] + + def __init__(self, **kwargs): + super(DigestAlgorithmMustNotValidate, self).__init__(**kwargs) + self.template_kwargs['algorithm_text'] = fmt.DS_DIGEST_TYPES.get(self.template_kwargs['algorithm'], self.template_kwargs['algorithm']) + class DNSKEYRevokedDS(DSDigestError): ''' >>> e = DNSKEYRevokedDS() diff --git a/dnsviz/analysis/status.py b/dnsviz/analysis/status.py index a2197a8..5ba2595 100644 --- a/dnsviz/analysis/status.py +++ b/dnsviz/analysis/status.py @@ -169,6 +169,16 @@ RRSIG_SIG_LENGTH_ERRORS = { DS_DIGEST_ALGS_STRONGER_THAN_SHA1 = (2, 4) DS_DIGEST_ALGS_IGNORING_SHA1 = (2,) +# RFC 8624 Section 3.1 +DNSKEY_ALGS_NOT_RECOMMENDED = (5, 7, 10) +DNSKEY_ALGS_MUST_NOT_SIGN = (1, 3, 6, 12) +DNSKEY_ALGS_MUST_NOT_VALIDATE = (1, 3, 6) + +# RFC 8624 Section 3.2 +DS_DIGEST_ALGS_NOT_RECOMMENDED = () +DS_DIGEST_ALGS_MUST_NOT_SIGN = (0, 1, 3) +DS_DIGEST_ALGS_MUST_NOT_VALIDATE = () + class RRSIGStatus(object): def __init__(self, rrset, rrsig, dnskey, zone_name, reference_ts, supported_algs): self.rrset = rrset @@ -186,13 +196,37 @@ class RRSIGStatus(object): self.validation_status = RRSIG_STATUS_VALID if self.signature_valid is None or self.dnskey.rdata.algorithm not in supported_algs: + # Either we can't validate the cryptographic signature, or we are + # explicitly directed to ignore the algorithm. if self.dnskey is None: + # In this case, there is no corresponding DNSKEY, so we make + # the status "INDETERMINATE". if self.validation_status == RRSIG_STATUS_VALID: self.validation_status = RRSIG_STATUS_INDETERMINATE_NO_DNSKEY + else: - if self.validation_status == RRSIG_STATUS_VALID: - self.validation_status = RRSIG_STATUS_INDETERMINATE_UNKNOWN_ALGORITHM - self.warnings.append(Errors.AlgorithmNotSupported(algorithm=self.rrsig.algorithm)) + # If there is a DNSKEY, then we look at *why* we are ignoring + # the cryptographic signature. + if self.dnskey.rdata.algorithm in DNSKEY_ALGS_MUST_NOT_VALIDATE: + # In this case, specification dictates that the algorithm + # MUST NOT be validated, so we mark it as ignored. + if self.validation_status == RRSIG_STATUS_VALID: + self.validation_status = RRSIG_STATUS_ALGORITHM_IGNORED + else: + # In this case, we can't validate this particular + # algorithm, either because the code doesn't support it, + # or because we have been explicitly directed to ignore it. + # In either case, mark it as "UNKNOWN", and warn that it is + # not supported. + if self.validation_status == RRSIG_STATUS_VALID: + self.validation_status = RRSIG_STATUS_INDETERMINATE_UNKNOWN_ALGORITHM + self.warnings.append(Errors.AlgorithmNotSupported(algorithm=self.rrsig.algorithm)) + + # Independent of whether or not we considered the cryptographic + # validation, issue a warning if we are using an algorithm for which + # validation has been prohibited. + if self.dnskey.rdata.algorithm in DNSKEY_ALGS_MUST_NOT_VALIDATE: + self.warnings.append(Errors.AlgorithmMustNotValidate(algorithm=self.rrsig.algorithm)) if self.rrset.ttl_cmp: if self.rrset.rrset.ttl != self.rrset.rrsig_info[self.rrsig].ttl: @@ -351,13 +385,36 @@ class DSStatus(object): self.validation_status = DS_STATUS_VALID if self.digest_valid is None or self.ds.digest_type not in supported_digest_algs: + # Either we cannot reproduce a digest with this type, or we are + # explicitly directed to ignore the digest type. if self.dnskey is None: + # In this case, there is no corresponding DNSKEY, so we make + # the status "INDETERMINATE". if self.validation_status == DS_STATUS_VALID: self.validation_status = DS_STATUS_INDETERMINATE_NO_DNSKEY else: - if self.validation_status == DS_STATUS_VALID: - self.validation_status = DS_STATUS_INDETERMINATE_UNKNOWN_ALGORITHM - self.warnings.append(Errors.DigestAlgorithmNotSupported(algorithm=ds.digest_type)) + # If there is a DNSKEY, then we look at *why* we are ignoring + # the digest of the DNSKEY. + if self.ds.digest_type in DS_DIGEST_ALGS_MUST_NOT_VALIDATE: + # In this case, specification dictates that the algorithm + # MUST NOT be validated, so we mark it as ignored. + if self.validation_status == DS_STATUS_VALID: + self.validation_status = DS_STATUS_ALGORITHM_IGNORED + else: + # In this case, we can't validate this particular + # digest type, either because the code doesn't support it, + # or because we have been explicitly directed to ignore it. + # In either case, mark it as "UNKNOWN", and warn that it is + # not supported. + if self.validation_status == DS_STATUS_VALID: + self.validation_status = DS_STATUS_INDETERMINATE_UNKNOWN_ALGORITHM + self.warnings.append(Errors.DigestAlgorithmNotSupported(algorithm=self.ds.digest_type)) + + # Independent of whether or not we considered the digest for + # validation, issue a warning if we are using a digest type for which + # validation has been prohibited. + if self.ds.digest_type in DS_DIGEST_ALGS_MUST_NOT_VALIDATE: + self.warnings.append(Errors.DigestAlgorithmMustNotValidate(algorithm=self.ds.digest_type)) if self.dnskey is not None and \ self.dnskey.rdata.flags & fmt.DNSKEY_FLAGS['revoke']: |