Skip to content

Commit

Permalink
feat: added DnsErrors and decrypting
Browse files Browse the repository at this point in the history
  • Loading branch information
alonsovch committed Jul 12, 2024
1 parent 9d6d40e commit 549c321
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 174 deletions.
78 changes: 26 additions & 52 deletions src/dnssec/dnssec.rs
Original file line number Diff line number Diff line change
@@ -1,60 +1,34 @@
// src/dnssec.rs
use crate::client::ClientUDPConnection;
use crate::message::{DnsMessage, ResourceRecord};
use crate::dnssec_message_processing::extract_dnssec_records;
use crate::rrset_signature::{verify_rrsig, verify_ds};
use crate::dnskey_rdata::DnskeyRdata;
use crate::client::client_error::ClientError;
use std::net::IpAddr;
use tokio::time::Duration;

pub mod dnssec {
use super::message::{DnskeyRdata, RrsigRdata, DsRdata, DnsMessage, DnsError};
use sha2::{Sha256, Sha512, Digest};
use hmac::{Hmac, Mac, NewMac};
use rust_crypto::rsa::{RSAPublicKey, RSAVerify};
use base64::decode;
pub async fn fetch_dnskey_records(domain: &str, server_addr: IpAddr, timeout_duration: Duration) -> Result<Vec<DnskeyRdata>, ClientError> {
let conn = ClientUDPConnection::new(server_addr, timeout_duration);

pub fn fetch_dnskey_records(domain: &str) -> Result<Vec<DnskeyRdata>, DnsError> {
// Completar
Ok(vec![DnskeyRdata::new(256, 3, 8, vec![0x03, 0x01, 0x00, 0x01])])
}

pub fn verify_rrsig_signature(
rrsig: &RrsigRdata,
dnskey: &DnskeyRdata,
signed_data: &[u8]
) -> Result<bool, DnsError> {
rsa_verify(&dnskey.public_key, &rrsig.signature, signed_data)
}
let dns_query = DnsMessage::new_query_message(
domain.into(),
Qtype::DNSKEY,
Qclass::IN,
0,
false,
1,
);

pub fn verify_ds_record(
ds: &DsRdata,
dnskey: &DnskeyRdata
) -> Result<bool, DnsError> {
let digest = compute_digest(ds.algorithm, &dnskey.to_bytes())?;
Ok(digest == ds.digest)
}
let response = conn.send(dns_query).await?;

fn rsa_verify(
public_key: &[u8],
signature: &[u8],
signed_data: &[u8]
) -> Result<bool, DnsError> {
let public_key = RSAPublicKey::from_der(public_key).map_err(|_| DnsError::VerificationFailed)?;
public_key.verify(signature, signed_data).map_err(|_| DnsError::VerificationFailed)?;
let dns_response = DnsMessage::from_bytes(&response)?;
let mut dnskey_records = Vec::new();

Ok(true)
}

fn compute_digest(
algorithm: u8,
data: &[u8]
) -> Result<Vec<u8>, DnsError> {
match algorithm {
1 => {
let mut hasher = Sha256::new();
hasher.update(data);
Ok(hasher.finalize().to_vec())
},
2 => {
let mut hasher = Sha512::new();
hasher.update(data);
Ok(hasher.finalize().to_vec())
},
_ => Err(DnsError::UnsupportedAlgorithm),
for record in dns_response.get_answer() {
if let Rdata::DNSKEY(dnskey) = &record.get_rdata() {
dnskey_records.push(dnskey.clone());
}
}

Ok(dnskey_records)
}
44 changes: 12 additions & 32 deletions src/dnssec/dnssec_message_processing.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,19 @@
use crate::message::DnsMessage;
use crate::message::rdata::{rrsig_rdata, dnskey_rdata, ds_rdata, nsec_rdata, nsec3_rdata};
use crate::message::{DnsMessage, Rdata, ResourceRecord};

// DNSKEY, RRSIG, DS | NSEC, NSEC3 ?
pub fn extract_dnssec_records(dns_response: &DnsMessage) -> (Vec<ResourceRecord>, Vec<ResourceRecord>) {
let answers = dns_response.get_answer();
let additionals = dns_response.get_additional();

fn extract_dnssec_records(dns_message: &DnsMessage) -> (Vec<DnskeyRdata>, Vec<RRSIGRdata>, Vec<DsRdata>) {
let mut dnskey_records = Vec::new();
let mut rrsig_records = Vec::new();
let mut ds_records = Vec::new();

for record in &dns_message.additional {
match record.rdata {
Rdata::DNSKEY(ref data) => {
let dnskey_rdata = DnskeyRdata::new(data.flags, data.protocol, data.algorithm, data.public_key.clone());
dnskey_records.push(dnskey_rdata);
}
Rdata::RRSIG(ref data) => {
let rrsig_rdata = RrsigRdata::new(
data.type_covered,
data.algorithm,
data.labels,
data.original_ttl,
//datatime? expiration/inception
data.key_tag,
data.signer_name.clone(),
data.signature.clone()
);
rrsig_records.push(rrsig_rdata);
}
Rdata::DS(ref data) => {
let ds_rdata = DsRdata::new(data.key_tag, data.algorithm, data.digest_type, data.digest.clone()
);
ds_records.push(ds_rdata);
},
_ => (),
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, ds_records)
}

(dnskey_records, rrsig_records)
}
148 changes: 70 additions & 78 deletions src/dnssec/rrset_signature.rs
Original file line number Diff line number Diff line change
@@ -1,87 +1,79 @@
use crypto::digest::Digest;
use crypto::sha1::Sha1;
use crypto::sha2::Sha256;
use crypto::rsa::Rsa;
use crate::dnssec::RRset;
use crate::dnskey_rdata::DnskeyRdata;
use crate::rrsig_rdata::RRSIGRdata;
use base64;
use sha2::{Sha256, Digest};
use rust_crypto::digest::Digest as RustDigest;
use rust_crypto::sha1::Sha1;
use base64::encode;
use crate::message::rdata::{DnskeyRdata, RrsigRdata, Rdata};
use crate::message::resource_record::ResourceRecord;
use crate::client::client_error::ClientError;

/// RFCs: [4033, 4034, 4035, 4509]
pub fn verify_rrsig(rrsig: &RrsigRdata, dnskey: &DnskeyRdata, rrset: &[ResourceRecord]) -> Result<bool, ClientError> {
let mut rrsig_data = Vec::new();
rrsig_data.extend_from_slice(&rrsig.type_covered.to_be_bytes());
rrsig_data.push(rrsig.algorithm);
rrsig_data.push(rrsig.labels);
rrsig_data.extend_from_slice(&rrsig.original_ttl.to_be_bytes());
rrsig_data.extend_from_slice(&rrsig.expiration.to_be_bytes());
rrsig_data.extend_from_slice(&rrsig.inception.to_be_bytes());
rrsig_data.extend_from_slice(&rrsig.key_tag.to_be_bytes());
rrsig_data.extend_from_slice(rrsig.signer_name.to_bytes()?);

/// Signs a RRset using the private_key given.
/// Returns a RRSIG that contains the signature.
pub fn sign_rrset(rrset: &RRset, private_key: &Rsa, algorithm: u8) -> Result<RRSIGRdata, &'static str> {
let rrset_bytes = rrset.to_bytes();

let mut hasher: Box<dyn Digest> = match algorithm {
1 => Box::new(Sha1::new()),
2 => Box::new(Sha256::new()),
_ => return Err("Algorithm not supported"),
};

hasher.input(&rrset_bytes);
let hash = hasher.result_str();

let signature = private_key.sign(hasher, &hash.as_bytes()).map_err(|_| "Error while signing")?;

let rrsig = RRSIGRdata {
type_covered: rrset.get_type(),
algorithm,
labels: rrset.get_labels(),
original_ttl: rrset.get_ttl(),
signature_expiration: get_expiration_time(),
signature_inception: get_inception_time(),
key_tag: calculate_key_tag(private_key),
signer_name: rrset.get_name().clone(),
signature: base64::encode(&signature),
};

Ok(rrsig)
}
let mut rrset_sorted = rrset.to_vec();
rrset_sorted.sort_by(|a, b| a.name.cmp(&b.name));

// Gets the expiration time for the signature.
fn get_expiration_time() -> u32 {
// Supposing sign validity = 1 day (86400 seconds)
let now = std::time::SystemTime::now();
let expiration_duration = std::time::Duration::new(86400, 0);
let expiration_time = now + expiration_duration;
expiration_time.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as u32
}
for rr in rrset_sorted.iter() {
rrsig_data.extend_from_slice(rr.name.to_bytes()?);
rrsig_data.extend_from_slice(&rr.ttl.to_be_bytes());
rrsig_data.extend_from_slice(&(rr.rdata.to_bytes().len() as u16).to_be_bytes());
rrsig_data.extend_from_slice(&rr.rdata.to_bytes()?);
}

// Gets the inception time for the signature.
fn get_inception_time() -> u32 {
// Assuming current time
let now = std::time::SystemTime::now();
now.duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as u32
}
let signature = rrsig.signature.clone();
let mut hasher = Sha256::new();
hasher.update(rrsig_data);
let hashed = hasher.finalize();

// Calculates the key tag for the public key.
fn calculate_key_tag(private_key: &Rsa) -> u16 {
let public_key_der = private_key.to_public_key_der().unwrap();
let mut hasher = Sha1::new();
hasher.input(&public_key_der);
let hash = hasher.result_str();
u16::from_be_bytes([hash.as_bytes()[18], hash.as_bytes()[19]])
match dnskey.algorithm {
3 => {
//DSA/SHA1
let mut sha1 = Sha1::new();
sha1.input(&rrsig_data);
let digest = sha1.result_str();
Ok(digest == encode(&signature))
},
5 => {
//RSA/SHA1
let mut sha1 = Sha1::new();
sha1.input(&rrsig_data);
let digest = sha1.result_str();
Ok(digest == encode(&signature))
},
8 => {
//RSA/SHA256
let hashed = Sha256::digest(&rrsig_data);
Ok(encode(&hashed) == encode(&signature))
},
_ => Err(ClientError::new("Unknown DNSKEY algorithm")),
}
}

/// RFCs: [4033, 4034, 4035, 5702]
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()?;
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::new("Unknown DS algorithm")),
};

/// Verifies an RRset using the provided public key and RRSIG record.
/// Returns true if the verification is successful.
pub fn verify_rrset(rrset: &RRset, rrsig: &RRSIGRdata, public_key: &Rsa) -> Result<bool, &'static str> {
let rrset_bytes = rrset.to_bytes();

let mut hasher: Box<dyn Digest> = match rrsig.algorithm {
1 => Box::new(Sha1::new()),
2 => Box::new(Sha256::new()),
_ => return Err("Algorithm not supported"),
};

hasher.input(&rrset_bytes);
let hash = hasher.result_str();

let signature = base64::decode(&rrsig.signature).map_err(|_| "Error while decoding signature")?;

public_key.verify(hasher, &hash.as_bytes(), &signature).map_err(|_| "Verification failed")
Ok(ds_rdata.digest == hashed_key)
} else {
Err(ClientError::new("Provided record is not a DS record"))
}
}
12 changes: 0 additions & 12 deletions src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -894,18 +894,6 @@ impl DnsMessage {
}
}

pub fn extract_dnssec_records(&self) -> Vec<DnskeyRdata> {
let mut dnssec_records = Vec::new();

for record in self.additional.iter() {
if let Rdata::DNSKEY(dnskey) = &record.get_rdata() {
dnssec_records.push(dnskey.clone());
}
}

dnssec_records
}

}

impl fmt::Display for DnsMessage {
Expand Down

0 comments on commit 549c321

Please sign in to comment.