-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dnssec-integration' into develop
- Loading branch information
Showing
27 changed files
with
2,272 additions
and
249 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub mod dnssec_message; | ||
pub mod dnssec_message_processing; | ||
pub mod dnssec_fetch; | ||
pub mod rrset_signature; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
use crate::message::DnsMessage; | ||
use crate::message::rdata::Rdata; | ||
use crate::message::rdata::dnskey_rdata::DnskeyRdata; | ||
use crate::message::resource_record::ResourceRecord; | ||
use crate::dnssec::dnssec_message_processing::extract_dnssec_records; | ||
use crate::dnssec::rrset_signature::{verify_rrsig, verify_ds}; | ||
|
||
use crate::client::client_error::ClientError; | ||
|
||
pub async fn fetch_dnskey_records(dns_response: &DnsMessage) -> Result<Vec<DnskeyRdata>, ClientError> { | ||
let mut dnskey_records = Vec::new(); | ||
|
||
for record in dns_response.get_answer() { | ||
if let Rdata::DNSKEY(dnskey) = &record.get_rdata() { | ||
dnskey_records.push(dnskey.clone()); | ||
} | ||
} | ||
|
||
Ok(dnskey_records) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
use std::str::FromStr; | ||
use crate::domain_name::DomainName; | ||
use crate::message::rclass::Rclass; | ||
use crate::message::DnsMessage; | ||
use crate::message::rdata::opt_rdata::OptRdata; | ||
use crate::message::rdata::Rdata; | ||
use crate::message::resource_record::{FromBytes, ResourceRecord, ToBytes}; | ||
use crate::message::rcode; | ||
use crate::message::rcode::Rcode; | ||
use crate::message::rrtype::Rrtype; | ||
|
||
const EDNS_VERSION: u8 = 0; | ||
const REQUESTED_UDP_LEN: u16 = 4096; | ||
/* | ||
The mechanism chosen for the explicit notification of the ability of | ||
the client to accept (if not understand) DNSSEC security RRs is using | ||
the most significant bit of the Z field on the EDNS0 OPT header in | ||
the query. This bit is referred to as the "DNSSEC OK" (DO) bit. In | ||
the context of the EDNS0 OPT meta-RR, the DO bit is the first bit of | ||
the third and fourth bytes of the "extended RCODE and flags" portion | ||
of the EDNS0 OPT meta-RR, structured as follows: | ||
+0 (MSB) +1 (LSB) | ||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ||
0: | EXTENDED-RCODE | VERSION | | ||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ||
2: |DO| Z | | ||
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ||
*/ | ||
fn create_opt_rr(capacity: u16 ,e_rcode :u8, version: u8, do_bit: bool) -> ResourceRecord { | ||
let opt_rdata = OptRdata::new(); | ||
let rdata = Rdata::OPT(opt_rdata); | ||
let mut rr = ResourceRecord::new(rdata); | ||
|
||
let do_val: u16 = if do_bit {0x8000} else {0x0}; | ||
let ttl: u32 = (e_rcode as u32) << 24 | (version as u32) << 16| (do_val as u32); | ||
rr.set_ttl(ttl); | ||
rr.set_rclass(Rclass::UNKNOWN(capacity)); | ||
println!("EL ttl es: {:#05x?}", ttl); | ||
rr | ||
} | ||
|
||
fn read_opt_rr(opt_rr: ResourceRecord) -> String { | ||
let requested_udp_len = Rclass::from(opt_rr.get_rclass()); | ||
let data = opt_rr.get_ttl().to_be_bytes(); | ||
let (e_rcode, version) = (data[0], data[1]); | ||
let z = u16::from_be_bytes([data[2], data[3]]); | ||
|
||
let do_bit = ((z & 0x8000) > 0) as u8 ; | ||
format!("OPT PSEUDO-RR\n\trequested_udp_len: {requested_udp_len}\n\terror code: {e_rcode}\n\tversion: EDNS{version}\n\tuse dnssec: {do_bit}") | ||
} | ||
|
||
/* | ||
A security-aware resolver MUST include an EDNS ([RFC2671]) OPT | ||
pseudo-RR with the DO ([RFC3225]) bit set when sending queries. | ||
*/ | ||
fn create_dns_message_with_dnssec(mut msg: DnsMessage) -> DnsMessage { | ||
// We create a opt rr with the do bit set to 1 | ||
// with NOERR as rcode and EDNS0 | ||
let rr = create_opt_rr(REQUESTED_UDP_LEN, | ||
Rcode::from(Rcode::NOERROR).into(), | ||
EDNS_VERSION, | ||
true); | ||
|
||
let vec = vec![rr]; | ||
msg.add_additionals(vec); | ||
msg | ||
} | ||
|
||
#[test] | ||
fn see_dnssec_message() { | ||
let query = DnsMessage::new_query_message( | ||
DomainName::new_from_str("example.com"), | ||
Rrtype::A, | ||
Rclass::UNKNOWN(4096), | ||
1, | ||
true, | ||
2000 | ||
); | ||
let query= create_dns_message_with_dnssec(query); | ||
assert_eq!(String::from_str | ||
("OPT PSEUDO-RR\n\trequested_udp_len: 4096\n\terror code: 0\n\tversion: EDNS0\n\tuse dnssec: 1") | ||
.expect("Not a utf8 str"), | ||
read_opt_rr(query.get_additional().pop().expect("No OPT Record!")) | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
use crate::message::DnsMessage; | ||
use crate::message::rdata::Rdata; | ||
use crate::message::resource_record::ResourceRecord; | ||
|
||
pub fn extract_dnssec_records(dns_response: &DnsMessage) -> (Vec<ResourceRecord>, Vec<ResourceRecord>) { | ||
let answers = dns_response.get_answer(); | ||
let additionals = dns_response.get_additional(); | ||
|
||
let mut dnskey_records = Vec::new(); | ||
let mut rrsig_records = Vec::new(); | ||
|
||
for record in answers.iter().chain(additionals.iter()) { | ||
match record.get_rdata() { | ||
Rdata::DNSKEY(_) => dnskey_records.push(record.clone()), | ||
Rdata::RRSIG(_) => rrsig_records.push(record.clone()), | ||
_ => {} | ||
} | ||
} | ||
|
||
(dnskey_records, rrsig_records) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
use sha2::{Sha256, Digest}; | ||
use crypto::digest::Digest as RustDigest; | ||
use crypto::sha1::Sha1; | ||
use base64::encode; | ||
use crate::message::rdata::Rdata; | ||
use crate::message::rdata::dnskey_rdata::DnskeyRdata; | ||
use crate::message::rdata::rrsig_rdata::RRSIGRdata; | ||
use crate::message::rrtype::Rrtype; | ||
use crate::message::resource_record::{ResourceRecord, ToBytes}; | ||
use crate::client::client_error::ClientError; | ||
|
||
pub fn verify_rrsig(rrsig: &RRSIGRdata, dnskey: &DnskeyRdata, rrset: &[ResourceRecord]) -> Result<bool, ClientError> { | ||
let mut rrsig_data = Vec::new(); | ||
rrsig_data.extend_from_slice(&u16::from(rrsig.get_type_covered()).to_be_bytes()); | ||
rrsig_data.push(rrsig.get_algorithm()); | ||
rrsig_data.push(rrsig.get_labels()); | ||
rrsig_data.extend_from_slice(&rrsig.get_original_ttl().to_be_bytes()); | ||
rrsig_data.extend_from_slice(&rrsig.get_signature_expiration().to_be_bytes()); | ||
rrsig_data.extend_from_slice(&rrsig.get_signature_inception().to_be_bytes()); | ||
rrsig_data.extend_from_slice(&rrsig.get_key_tag().to_be_bytes()); | ||
rrsig_data.extend_from_slice(&rrsig.get_signer_name().to_bytes());//Try? | ||
|
||
let mut rrset_sorted = rrset.to_vec(); | ||
rrset_sorted.sort_by(|a, b| a.get_name().cmp(&b.get_name())); | ||
|
||
for rr in rrset_sorted.iter() { | ||
rrsig_data.extend_from_slice(&rr.get_name().to_bytes()); //Try? | ||
rrsig_data.extend_from_slice(&rr.get_ttl().to_be_bytes()); | ||
rrsig_data.extend_from_slice(&(rr.get_rdata().to_bytes().len() as u16).to_be_bytes()); | ||
rrsig_data.extend_from_slice(&rr.get_rdata().to_bytes());//Try? | ||
} | ||
|
||
let signature = rrsig.get_signature().clone(); | ||
let hashed = Sha256::digest(&rrsig_data); | ||
|
||
match dnskey.algorithm { | ||
3 | 5 => { | ||
// (DSA/RSA)/SHA1 | ||
let mut sha1 = Sha1::new(); | ||
sha1.input(&rrsig_data); | ||
let digest = sha1.result_str(); | ||
Ok(digest == encode(&signature)) | ||
}, | ||
8 => { | ||
// RSA/SHA256 | ||
Ok(encode(&hashed) == encode(&signature)) | ||
}, | ||
_ => Err(ClientError::NotImplemented("Unknown DNSKEY algorithm")), | ||
} | ||
} | ||
|
||
pub fn verify_ds(ds_record: &ResourceRecord, dnskey: &DnskeyRdata) -> Result<bool, ClientError> { | ||
if let Rdata::DS(ds_rdata) = &ds_record.get_rdata() { | ||
let dnskey_bytes = dnskey.to_bytes(); //Try? | ||
let hashed_key = match ds_rdata.algorithm { | ||
1 => { | ||
let mut hasher = Sha1::new(); | ||
hasher.input(&dnskey_bytes); | ||
hasher.result_str() | ||
}, | ||
2 => { | ||
let hashed = Sha256::digest(&dnskey_bytes); | ||
encode(&hashed) | ||
}, | ||
_ => return Err(ClientError::NotImplemented("Unknown DS algorithm")), | ||
}; | ||
|
||
Ok(ds_rdata.digest == hashed_key.as_bytes()) | ||
} else { | ||
Err(ClientError::FormatError("Provided record is not a DS record")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.