-
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 pull request #15 from niclabs/decrypt-signature
Decrypt signature
- Loading branch information
Showing
8 changed files
with
525 additions
and
150 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
pub mod dnssec_encryption; | ||
pub mod dnssec_decryption; | ||
|
||
|
||
use std::str::FromStr; | ||
|
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,17 @@ | ||
use crate::message::{DnsMessage, Rdata, ResourceRecord}; | ||
use crate::dnssec_message_processing::extract_dnssec_records; | ||
use crate::rrset_signature::{verify_rrsig, verify_ds}; | ||
use crate::message::rdata::DnskeyRdata; | ||
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 was deleted.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
use crate::message::{DnsMessage, Rdata, 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,69 @@ | ||
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; | ||
|
||
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()?); | ||
|
||
let mut rrset_sorted = rrset.to_vec(); | ||
rrset_sorted.sort_by(|a, b| a.name.cmp(&b.name)); | ||
|
||
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()?); | ||
} | ||
|
||
let signature = rrsig.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::new("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()?; | ||
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")), | ||
}; | ||
|
||
Ok(ds_rdata.digest == hashed_key) | ||
} else { | ||
Err(ClientError::new("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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
use crate::message::Rtype; | ||
use crate::message::Rclass; | ||
use crate::message::resource_record::ResourceRecord; | ||
use std::collections::HashSet; | ||
|
||
/// Represents a set of resource records (RRset). | ||
#[derive(Debug)] | ||
pub struct RRset { | ||
/// The name of the domain associated with this RRset. | ||
name: String, | ||
/// The type of resource record in this RRset. | ||
rtype: Rtype, | ||
/// The class of resource record in this RRset. | ||
rclass: Rclass, | ||
/// The time to live (TTL) value for records in this RRset. | ||
ttl: u32, | ||
/// The set of resource records belonging to this RRset. | ||
records: HashSet<ResourceRecord>, | ||
} | ||
|
||
impl RRset { | ||
/// Creates a new RRset. | ||
pub fn new(name: String, rtype: Rtype, rclass: Rclass, ttl: u32) -> RRset { | ||
RRset { | ||
name, | ||
rtype, | ||
rclass, | ||
ttl, | ||
records: HashSet::new(), | ||
} | ||
} | ||
|
||
/// Adds a resource record to this RRset. | ||
pub fn add_record(&mut self, record: ResourceRecord) { | ||
self.records.insert(record); | ||
} | ||
|
||
/// Gets the name of the domain associated with this RRset. | ||
pub fn get_name(&self) -> &String { | ||
&self.name | ||
} | ||
|
||
/// Gets the type of resource record in this RRset. | ||
pub fn get_type(&self) -> Rtype { | ||
self.rtype | ||
} | ||
|
||
/// Gets the class of resource record in this RRset. | ||
pub fn get_class(&self) -> Rclass { | ||
self.rclass | ||
} | ||
|
||
/// Gets the time to live (TTL) value for records in this RRset. | ||
pub fn get_ttl(&self) -> u32 { | ||
self.ttl | ||
} | ||
|
||
/// Gets the set of resource records belonging to this RRset. | ||
pub fn get_records(&self) -> &HashSet<ResourceRecord> { | ||
&self.records | ||
} | ||
|
||
/// Gets the labels of the domain associated with this RRset. | ||
pub fn get_labels(&self) -> usize { | ||
self.name.split('.').count() | ||
} | ||
|
||
/// Serializes the RRset to a byte array for signing. | ||
pub fn to_bytes(&self) -> Vec<u8> { | ||
let mut bytes = Vec::new(); | ||
for record in &self.records { | ||
bytes.extend(record.to_bytes()); // Assuming ResourceRecord has a to_bytes method | ||
} | ||
bytes | ||
} | ||
} | ||
|
||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::message::Rtype; | ||
use crate::message::Rclass; | ||
use crate::message::resource_record::{ResourceRecord, Rdata, ARdata, NsRdata, CnameRdata}; | ||
use std::net::IpAddr; | ||
use std::collections::HashSet; | ||
|
||
#[test] | ||
fn test_create_rrset() { | ||
let name = "example.com".to_string(); | ||
let rtype = Rtype::A; | ||
let rclass = Rclass::IN; | ||
let ttl = 3600; | ||
|
||
let rrset = RRset::new(name.clone(), rtype, rclass, ttl); | ||
|
||
assert_eq!(rrset.get_name(), &name); | ||
assert_eq!(rrset.get_type(), Rtype::A); | ||
assert_eq!(rrset.get_class(), Rclass::IN); | ||
assert_eq!(rrset.get_ttl(), 3600); | ||
assert_eq!(rrset.get_labels(), 2); | ||
assert!(rrset.get_records().is_empty()); | ||
} | ||
|
||
#[test] | ||
fn test_add_record() { | ||
let name = "example.com".to_string(); | ||
let rtype = Rtype::A; | ||
let rclass = Rclass::IN; | ||
let ttl = 3600; | ||
|
||
let mut rrset = RRset::new(name.clone(), rtype, rclass, ttl); | ||
|
||
let mut a_rdata = Rdata::A(ARdata::new()); | ||
match a_rdata { | ||
Rdata::A(ref mut val) => val.set_address(IpAddr::from([127, 0, 0, 1])), | ||
_ => unreachable!(), | ||
} | ||
|
||
let record = ResourceRecord::new(a_rdata); | ||
rrset.add_record(record); | ||
|
||
assert_eq!(rrset.get_records().len(), 1); | ||
} | ||
|
||
#[test] | ||
fn test_get_name() { | ||
let name = "example.com".to_string(); | ||
let rtype = Rtype::A; | ||
let rclass = Rclass::IN; | ||
let ttl = 3600; | ||
|
||
let rrset = RRset::new(name.clone(), rtype, rclass, ttl); | ||
|
||
assert_eq!(rrset.get_name(), &name); | ||
} | ||
|
||
#[test] | ||
fn test_get_type() { | ||
let name = "example.com".to_string(); | ||
let rtype = Rtype::NS; | ||
let rclass = Rclass::IN; | ||
let ttl = 3600; | ||
|
||
let rrset = RRset::new(name.clone(), rtype, rclass, ttl); | ||
|
||
assert_eq!(rrset.get_type(), Rtype::NS); | ||
} | ||
|
||
#[test] | ||
fn test_get_class() { | ||
let name = "example.com".to_string(); | ||
let rtype = Rtype::MX; | ||
let rclass = Rclass::CH; | ||
let ttl = 3600; | ||
|
||
let rrset = RRset::new(name.clone(), rtype, rclass, ttl); | ||
|
||
assert_eq!(rrset.get_class(), Rclass::CH); | ||
} | ||
|
||
#[test] | ||
fn test_get_ttl() { | ||
let name = "example.com".to_string(); | ||
let rtype = Rtype::A; | ||
let rclass = Rclass::IN; | ||
let ttl = 7200; | ||
|
||
let rrset = RRset::new(name.clone(), rtype, rclass, ttl); | ||
|
||
assert_eq!(rrset.get_ttl(), 7200); | ||
} | ||
|
||
#[test] | ||
fn test_get_labels() { | ||
let name = "sub.example.com".to_string(); | ||
let rtype = Rtype::A; | ||
let rclass = Rclass::IN; | ||
let ttl = 3600; | ||
|
||
let rrset = RRset::new(name.clone(), rtype, rclass, ttl); | ||
|
||
assert_eq!(rrset.get_labels(), 3); | ||
} | ||
} |