Allow validating SHA1 DS records
authorMatt Corallo <git@bluematt.me>
Thu, 8 Feb 2024 23:53:29 +0000 (23:53 +0000)
committerMatt Corallo <git@bluematt.me>
Thu, 8 Feb 2024 23:53:29 +0000 (23:53 +0000)
While these really shouldn't be used, they sometimes are, and
importantly we don't allow them for RRSig signature validation,
ensuring that if we find a SHA1 DS record it really is what was
meant in the parent zone and wasn't forged.

src/query.rs
src/validation.rs

index 6f68e09707b210798e7e9f846a528bdadbc9fa1b..adf687a176c87f7fd733a6ffff637ce5ec813d1e 100644 (file)
@@ -217,6 +217,22 @@ mod tests {
                assert!(verified_rrs.expires > now);
        }
 
+       #[test]
+       fn test_sha1_query() {
+               let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
+               let query_name = "benthecarman.com.".try_into().unwrap();
+               let proof = build_a_proof(sockaddr, &query_name).unwrap();
+
+               let mut rrs = parse_rr_stream(&proof).unwrap();
+               rrs.shuffle(&mut rand::rngs::OsRng);
+               let verified_rrs = verify_rr_stream(&rrs).unwrap();
+               assert!(verified_rrs.verified_rrs.len() >= 1);
+
+               let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
+               assert!(verified_rrs.valid_from < now);
+               assert!(verified_rrs.expires > now);
+       }
+
        #[test]
        fn test_txt_query() {
                let sockaddr = "8.8.8.8:53".to_socket_addrs().unwrap().next().unwrap();
index 999f7878cf9109d98e6ffb771980693067e33516..615146eb399648812fd7edf1809cceb96389a086 100644 (file)
@@ -162,7 +162,7 @@ where T: IntoIterator<IntoIter = I>, I: Iterator<Item = &'a DS> + Clone {
        let mut had_ds = false;
        for ds in dses.clone() {
                had_ds = true;
-               if ds.digest_type == 2 || ds.digest_type == 4 {
+               if ds.digest_type == 1 || ds.digest_type == 2 || ds.digest_type == 4 {
                        had_known_digest_type = true;
                        break;
                }
@@ -171,11 +171,13 @@ where T: IntoIterator<IntoIter = I>, I: Iterator<Item = &'a DS> + Clone {
        if !had_known_digest_type { return Err(ValidationError::UnsupportedAlgorithm); }
 
        for dnskey in records.iter() {
+               // Only use SHA1 DS records if we don't have any SHA256/SHA384 DS RRs.
+               let trust_sha1 = dses.clone().all(|ds| ds.digest_type != 2 && ds.digest_type != 4);
                for ds in dses.clone() {
-                       if ds.digest_type != 2 && ds.digest_type != 4 { continue; }
                        if ds.alg != dnskey.alg { continue; }
                        if dnskey.key_tag() == ds.key_tag {
                                let alg = match ds.digest_type {
+                                       1 if trust_sha1 => &ring::digest::SHA1_FOR_LEGACY_USE_ONLY,
                                        2 => &ring::digest::SHA256,
                                        4 => &ring::digest::SHA384,
                                        _ => continue,