use core::convert::{AsRef, From};
use core::fmt::Display;
use core::marker::Send;
use std::boxed::Box;
use std::cmp::Ordering;
use std::fmt::Debug;
use std::vec::Vec;
use octseq::builder::FromBuilder;
use octseq::{OctetsFrom, OctetsInto};
use tracing::debug;
use crate::base::cmp::CanonicalOrd;
use crate::base::iana::Rtype;
use crate::base::name::ToName;
use crate::base::rdata::{ComposeRecordData, RecordData};
use crate::base::record::Record;
use crate::base::Name;
use crate::crypto::sign::SignRaw;
use crate::dnssec::sign::error::SigningError;
use crate::dnssec::sign::keys::signingkey::SigningKey;
use crate::dnssec::sign::records::{RecordsIter, Rrset};
use crate::rdata::dnssec::{ProtoRrsig, Timestamp};
use crate::rdata::{Rrsig, ZoneRecordData};
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct GenerateRrsigConfig {
pub inception: Timestamp,
pub expiration: Timestamp,
}
impl GenerateRrsigConfig {
pub fn new(inception: Timestamp, expiration: Timestamp) -> Self {
Self {
inception,
expiration,
}
}
}
#[allow(clippy::type_complexity)]
pub fn sign_sorted_zone_records<N, Octs, Inner>(
apex_owner: &N,
mut records: RecordsIter<'_, N, ZoneRecordData<Octs, N>>,
keys: &[&SigningKey<Octs, Inner>],
config: &GenerateRrsigConfig,
) -> Result<Vec<Record<N, Rrsig<Octs, N>>>, SigningError>
where
Inner: Debug + SignRaw,
N: ToName
+ PartialEq
+ Clone
+ Debug
+ Display
+ Send
+ CanonicalOrd
+ From<Name<Octs>>,
Octs: AsRef<[u8]>
+ Debug
+ From<Box<[u8]>>
+ Send
+ OctetsFrom<Vec<u8>>
+ Clone
+ FromBuilder
+ From<&'static [u8]>,
{
let mut rrsigs = Vec::new();
let mut reusable_scratch = Vec::new();
let mut cut: Option<N> = None;
records.skip_before(apex_owner);
for owner_rrs in records {
if !owner_rrs.is_in_zone(apex_owner) {
break;
}
if let Some(ref cut) = cut {
if owner_rrs.owner().ends_with(cut) {
continue;
}
}
let name = owner_rrs.owner().clone();
cut = if owner_rrs.is_zone_cut(apex_owner) {
Some(name.clone())
} else {
None
};
for rrset in owner_rrs.rrsets() {
if cut.is_some() {
if rrset.rtype() != Rtype::DS && rrset.rtype() != Rtype::NSEC
{
continue;
}
} else if (rrset.rtype() == Rtype::DNSKEY
|| rrset.rtype() == Rtype::CDS
|| rrset.rtype() == Rtype::CDNSKEY)
&& name.canonical_cmp(apex_owner) == Ordering::Equal
{
continue;
} else {
if rrset.rtype() == Rtype::RRSIG {
continue;
}
}
for key in keys {
let inception = config.inception;
let expiration = config.expiration;
let rrsig_rr = sign_sorted_rrset_in(
key,
&rrset,
inception,
expiration,
&mut reusable_scratch,
)?;
rrsigs.push(rrsig_rr);
debug!(
"Signed {} RRSET at {} with keytag {}",
rrset.rtype(),
rrset.owner(),
key.dnskey().key_tag()
);
}
}
}
debug!(
"Returning {} RRSIG RRs from signature generation",
rrsigs.len(),
);
Ok(rrsigs)
}
pub fn sign_rrset<N, D, Octs, Inner>(
key: &SigningKey<Octs, Inner>,
rrset: &Rrset<'_, N, D>,
inception: Timestamp,
expiration: Timestamp,
) -> Result<Record<N, Rrsig<Octs, N>>, SigningError>
where
N: ToName + Debug + Clone + From<Name<Octs>>,
D: Clone + Debug + RecordData + ComposeRecordData + CanonicalOrd,
Inner: Debug + SignRaw,
Octs: AsRef<[u8]> + Clone + Debug + OctetsFrom<Vec<u8>>,
{
let mut records: Vec<_> = rrset.iter().collect();
records
.sort_by(|a, b| a.as_ref().data().canonical_cmp(b.as_ref().data()));
let rrset = Rrset::new_from_refs(&records)
.expect("records is not empty so new should not fail");
sign_sorted_rrset_in(key, &rrset, inception, expiration, &mut vec![])
}
pub fn sign_sorted_rrset_in<N, D, Octs, Inner>(
key: &SigningKey<Octs, Inner>,
rrset: &Rrset<'_, N, D>,
inception: Timestamp,
expiration: Timestamp,
scratch: &mut Vec<u8>,
) -> Result<Record<N, Rrsig<Octs, N>>, SigningError>
where
N: ToName + Clone + Debug + From<Name<Octs>>,
D: RecordData + Debug + ComposeRecordData + CanonicalOrd,
Inner: Debug + SignRaw,
Octs: AsRef<[u8]> + Clone + Debug + OctetsFrom<Vec<u8>>,
{
if rrset.rtype() == Rtype::RRSIG {
return Err(SigningError::RrsigRrsMustNotBeSigned);
}
if expiration < inception {
return Err(SigningError::InvalidSignatureValidityPeriod(
inception, expiration,
));
}
let rrsig = ProtoRrsig::new(
rrset.rtype(),
key.algorithm(),
rrset.owner().rrsig_label_count(),
rrset.ttl(),
expiration,
inception,
key.dnskey().key_tag(),
key.owner().clone().into(),
);
scratch.clear();
rrsig.compose_canonical(scratch).unwrap();
for record in rrset.iter() {
record.compose_canonical(scratch).unwrap();
}
let signature = key.raw_secret_key().sign_raw(&*scratch)?;
let signature = signature.as_ref().to_vec();
let Ok(signature) = signature.try_octets_into() else {
return Err(SigningError::OutOfMemory);
};
let rrsig = rrsig.into_rrsig(signature).expect("long signature");
debug_assert!(
(rrsig.labels() as usize) < rrset.owner().iter_labels().count()
);
Ok(Record::new(
rrset.owner().clone(),
rrset.class(),
rrset.ttl(),
rrsig,
))
}
#[cfg(test)]
mod tests {
use bytes::Bytes;
use core::str::FromStr;
use pretty_assertions::assert_eq;
use rand::RngExt;
use crate::base::iana::SecurityAlgorithm;
use crate::base::Serial;
use crate::crypto::sign::{KeyPair, SignError, Signature};
use crate::dnssec::sign::records::SortedRecords;
use crate::dnssec::sign::test_util;
use crate::dnssec::sign::test_util::*;
use crate::rdata::dnssec::Timestamp;
use crate::rdata::Dnskey;
use crate::zonetree::StoredName;
use super::*;
use crate::zonetree::types::StoredRecordData;
const TEST_INCEPTION: u32 = 0;
const TEST_EXPIRATION: u32 = 100;
#[test]
fn sign_rrset_adheres_to_rules_in_rfc_4034_and_rfc_4035() {
let apex_owner = Name::root();
let key = SigningKey::new(apex_owner.clone(), 0, TestKey::default());
let (inception, expiration) =
(Timestamp::from(0), Timestamp::from(0));
let mut records =
SortedRecords::<StoredName, StoredRecordData>::default();
records.insert(mk_a_rr("www.example.com.")).unwrap();
let rrset =
Rrset::new_from_owned(&records).expect("records is not empty");
let rrsig_rr =
sign_rrset(&key, &rrset, inception, expiration).unwrap();
let rrsig = rrsig_rr.data();
assert_eq!(rrsig_rr.owner(), rrset.owner());
assert_eq!(rrsig_rr.class(), rrset.class());
assert_eq!(rrsig.type_covered(), rrset.rtype());
assert_eq!(rrsig.original_ttl(), rrset.ttl());
assert_eq!(rrsig_rr.ttl(), rrset.ttl());
assert_eq!(rrsig.labels(), 3);
assert_eq!(rrsig.signer_name(), &apex_owner);
assert!((rrsig.labels() as usize) < rrset.owner().label_count());
}
#[test]
fn sign_rrset_with_wildcard() {
let apex_owner = Name::root();
let key = SigningKey::new(apex_owner.clone(), 0, TestKey::default());
let (inception, expiration) =
(Timestamp::from(0), Timestamp::from(0));
let mut records =
SortedRecords::<StoredName, StoredRecordData>::default();
records.insert(mk_a_rr("*.example.com.")).unwrap();
let rrset =
Rrset::new_from_owned(&records).expect("records is not empty");
let rrsig_rr =
sign_rrset(&key, &rrset, inception, expiration).unwrap();
let rrsig = rrsig_rr.data();
assert_eq!(rrsig.labels(), 2);
}
#[test]
fn sign_rrset_must_not_sign_rrsigs() {
let apex_owner = Name::root();
let key = SigningKey::new(apex_owner.clone(), 0, TestKey::default());
let (inception, expiration) =
(Timestamp::from(0), Timestamp::from(0));
let dnskey = key.dnskey().convert();
let mut records =
SortedRecords::<StoredName, StoredRecordData>::default();
records
.insert(mk_rrsig_rr("any.", Rtype::A, 1, ".", &dnskey))
.unwrap();
let rrset =
Rrset::new_from_owned(&records).expect("records is not empty");
let res = sign_rrset(&key, &rrset, inception, expiration);
assert!(matches!(res, Err(SigningError::RrsigRrsMustNotBeSigned)));
}
#[test]
fn sign_rrset_check_validity_period_handling() {
let apex_owner = Name::root();
let key = SigningKey::new(apex_owner.clone(), 0, TestKey::default());
let mut records =
SortedRecords::<StoredName, StoredRecordData>::default();
records.insert(mk_a_rr("any.")).unwrap();
let rrset =
Rrset::new_from_owned(&records).expect("records is not empty");
fn calc_timestamps(
start: u32,
duration: u32,
) -> (Timestamp, Timestamp) {
let start_serial = Serial::from(start);
let end = start_serial.add(duration).into_int();
(Timestamp::from(start), Timestamp::from(end))
}
let (inception, expiration) = calc_timestamps(5, 5);
sign_rrset(&key, &rrset, inception, expiration).unwrap();
let (inception, expiration) = calc_timestamps(10, 0);
sign_rrset(&key, &rrset, inception, expiration).unwrap();
let (expiration, inception) = calc_timestamps(5, 10);
let res = sign_rrset(&key, &rrset, inception, expiration);
assert!(matches!(
res,
Err(SigningError::InvalidSignatureValidityPeriod(_, _))
));
let (inception, expiration) = calc_timestamps(u32::MAX - 10, 10);
sign_rrset(&key, &rrset, inception, expiration).unwrap();
let (inception, expiration) = calc_timestamps(0, 10);
sign_rrset(&key, &rrset, inception, expiration).unwrap();
let (inception, expiration) = calc_timestamps(u32::MAX - 10, 20);
sign_rrset(&key, &rrset, inception, expiration).unwrap();
let sixty_eight_years_in_secs = 68 * 365 * 24 * 60 * 60;
let (inception, expiration) =
calc_timestamps(0, sixty_eight_years_in_secs);
sign_rrset(&key, &rrset, inception, expiration).unwrap();
let sixty_eight_years_in_secs = 68 * 365 * 24 * 60 * 60;
let one_year_in_secs = 365 * 24 * 60 * 60;
let (inception, expiration) = (
Timestamp::from(0),
Timestamp::from(sixty_eight_years_in_secs + one_year_in_secs),
);
let res = sign_rrset(&key, &rrset, inception, expiration);
assert!(matches!(
res,
Err(SigningError::InvalidSignatureValidityPeriod(_, _))
));
}
#[test]
fn generate_rrsigs_without_keys_should_succeed_for_empty_zone() {
let apex = Name::from_str("example.").unwrap();
let records =
SortedRecords::<StoredName, StoredRecordData>::default();
let no_keys: [&SigningKey<Bytes, KeyPair>; 0] = [];
sign_sorted_zone_records(
&apex,
RecordsIter::new_from_owned(&records),
&no_keys,
&GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
),
)
.unwrap();
}
#[test]
fn generate_rrsigs_without_keys_generates_no_rrsigs() {
let apex = Name::from_str("example.").unwrap();
let mut records = SortedRecords::default();
records.insert(mk_a_rr("example.")).unwrap();
let no_keys: [&SigningKey<Bytes, KeyPair>; 0] = [];
let rrsigs = sign_sorted_zone_records(
&apex,
RecordsIter::new_from_owned(&records),
&no_keys,
&GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
),
)
.unwrap();
assert!(rrsigs.is_empty());
}
#[test]
fn generate_rrsigs_for_partial_zone_at_apex() {
generate_rrsigs_for_partial_zone("example.", "example.");
}
#[test]
fn generate_rrsigs_for_partial_zone_beneath_apex() {
generate_rrsigs_for_partial_zone("example.", "in.example.");
}
fn generate_rrsigs_for_partial_zone(zone_apex: &str, record_owner: &str) {
let apex = Name::from_str(zone_apex).unwrap();
let mut records = SortedRecords::default();
records.insert(mk_a_rr(record_owner)).unwrap();
let keys = [&mk_dnssec_signing_key(true)];
let dnskey = keys[0].dnskey().convert();
let generated_records = sign_sorted_zone_records(
&apex,
RecordsIter::new_from_owned(&records),
&keys,
&GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
),
)
.unwrap();
let expected_labels = mk_name(record_owner).rrsig_label_count();
assert_eq!(generated_records.len(), 1);
assert_eq!(
generated_records[0],
mk_rrsig_rr(
record_owner,
Rtype::A,
expected_labels,
zone_apex,
&dnskey
)
);
}
#[test]
fn generate_rrsigs_ignores_records_outside_the_zone() {
let apex = Name::from_str("example.").unwrap();
let mut records = SortedRecords::default();
records.extend([
mk_soa_rr("example.", "mname.", "rname."),
mk_a_rr("in_zone.example."),
mk_a_rr("out_of_zone."),
]);
let keys = [&mk_dnssec_signing_key(true)];
let dnskey = keys[0].dnskey().convert();
let generated_records = sign_sorted_zone_records(
&apex,
RecordsIter::new_from_owned(&records),
&keys,
&GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
),
)
.unwrap();
assert_eq!(
generated_records,
[
mk_rrsig_rr("example.", Rtype::SOA, 1, "example.", &dnskey),
mk_rrsig_rr(
"in_zone.example.",
Rtype::A,
2,
"example.",
&dnskey
),
]
);
}
#[test]
fn generate_rrsigs_ignores_glue_records() {
let apex = Name::from_str("example.").unwrap();
let mut records = SortedRecords::default();
records.extend([
mk_soa_rr("example.", "mname.", "rname."),
mk_ns_rr("example.", "early_sorting_glue."),
mk_ns_rr("example.", "late_sorting_glue."),
mk_a_rr("in_zone.example."),
mk_a_rr("early_sorting_glue."),
mk_a_rr("late_sorting_glue."),
]);
let keys = [&mk_dnssec_signing_key(true)];
let dnskey = keys[0].dnskey().convert();
let generated_records = sign_sorted_zone_records(
&apex,
RecordsIter::new_from_owned(&records),
&keys,
&GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
),
)
.unwrap();
assert_eq!(
generated_records,
[
mk_rrsig_rr("example.", Rtype::NS, 1, "example.", &dnskey),
mk_rrsig_rr("example.", Rtype::SOA, 1, "example.", &dnskey),
mk_rrsig_rr(
"in_zone.example.",
Rtype::A,
2,
"example.",
&dnskey
),
]
);
}
#[test]
fn generate_rrsigs_for_complete_zone_with_csk() {
let keys = [&mk_dnssec_signing_key(true)];
let cfg = GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
);
generate_rrsigs_for_complete_zone(&keys, 0, 0, &cfg).unwrap();
}
#[test]
fn generate_rrsigs_for_complete_zone_with_only_zsk() {
let keys = [&mk_dnssec_signing_key(false)];
let cfg = GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
);
generate_rrsigs_for_complete_zone(&keys, 0, 0, &cfg).unwrap();
}
fn generate_rrsigs_for_complete_zone(
keys: &[&SigningKey<Bytes, TestKey>],
_ksk_idx: usize,
zsk_idx: usize,
cfg: &GenerateRrsigConfig,
) -> Result<(), SigningError> {
let zonefile = include_bytes!(
"../../../../test-data/zonefiles/rfc4035-appendix-A.zone"
);
let apex = Name::from_str("example.").unwrap();
let records = bytes_to_records(&zonefile[..]);
let generated_records = sign_sorted_zone_records(
&apex,
RecordsIter::new_from_owned(&records),
keys,
cfg,
)?;
let dnskeys = keys
.iter()
.map(|k| k.dnskey().convert())
.collect::<Vec<_>>();
let zsk = &dnskeys[zsk_idx];
let mut rrsig_iter = generated_records.iter();
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("example.", Rtype::NS, 1, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("example.", Rtype::SOA, 1, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("example.", Rtype::MX, 1, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("a.example.", Rtype::DS, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("ai.example.", Rtype::A, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("ai.example.", Rtype::HINFO, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("ai.example.", Rtype::AAAA, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("ns1.example.", Rtype::A, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("ns2.example.", Rtype::A, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("*.w.example.", Rtype::MX, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("x.w.example.", Rtype::MX, 3, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("x.y.w.example.", Rtype::MX, 4, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("xx.example.", Rtype::A, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("xx.example.", Rtype::HINFO, 2, "example.", zsk)
);
assert_eq!(
*rrsig_iter.next().unwrap(),
mk_rrsig_rr("xx.example.", Rtype::AAAA, 2, "example.", zsk)
);
assert!(rrsig_iter.next().is_none());
Ok(())
}
#[test]
fn generate_rrsigs_for_complete_zone_with_multiple_zsks() {
let apex = "example.";
let apex_owner = Name::from_str(apex).unwrap();
let mut records = SortedRecords::default();
records.extend([
mk_soa_rr(apex, "some.mname.", "some.rname."),
mk_ns_rr(apex, "ns.example."),
mk_a_rr("ns.example."),
]);
let keys =
[&mk_dnssec_signing_key(false), &mk_dnssec_signing_key(false)];
let zsk1 = keys[0].dnskey().convert();
let zsk2 = keys[1].dnskey().convert();
let generated_records = sign_sorted_zone_records(
&apex_owner,
RecordsIter::new_from_owned(&records),
&keys,
&GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
),
)
.unwrap();
assert_eq!(generated_records.len(), 6);
let it = generated_records
.iter()
.filter(|&rr| {
rr != &mk_rrsig_rr(apex, Rtype::SOA, 1, apex, &zsk1)
})
.filter(|&rr| {
rr != &mk_rrsig_rr(apex, Rtype::SOA, 1, apex, &zsk2)
})
.filter(|&rr| rr != &mk_rrsig_rr(apex, Rtype::NS, 1, apex, &zsk1))
.filter(|&rr| rr != &mk_rrsig_rr(apex, Rtype::NS, 1, apex, &zsk2))
.filter(|&rr| {
rr != &mk_rrsig_rr("ns.example.", Rtype::A, 2, apex, &zsk1)
})
.filter(|&rr| {
rr != &mk_rrsig_rr("ns.example.", Rtype::A, 2, apex, &zsk2)
});
let mut it = it.inspect(|rr| {
eprintln!(
"Warning: Unexpected RRSIG RRs remaining after filtering: {} {} => {:?}",
rr.owner(),
rr.rtype(),
rr.data(),
);
});
assert!(it.next().is_none());
}
#[test]
fn generate_rrsigs_for_already_signed_zone() {
let keys = [&mk_dnssec_signing_key(true)];
let dnskey = keys[0].dnskey().convert();
let apex = Name::from_str("example.").unwrap();
let mut records = SortedRecords::default();
records.extend([
mk_soa_rr("example.", "some.mname.", "some.rname."),
mk_ns_rr("example.", "ns.example."),
mk_dnskey_rr("example.", &dnskey),
mk_nsec_rr("example", "ns.example.", "SOA NS DNSKEY NSEC RRSIG"),
mk_rrsig_rr("example.", Rtype::SOA, 1, "example.", &dnskey),
mk_rrsig_rr("example.", Rtype::NS, 1, "example.", &dnskey),
mk_rrsig_rr("example.", Rtype::DNSKEY, 1, "example.", &dnskey),
mk_rrsig_rr("example.", Rtype::NSEC, 1, "example.", &dnskey),
mk_a_rr("ns.example."),
mk_nsec_rr("ns.example", "example.", "A NSEC RRSIG"),
mk_rrsig_rr("ns.example.", Rtype::A, 1, "example.", &dnskey),
mk_rrsig_rr("ns.example.", Rtype::NSEC, 1, "example.", &dnskey),
]);
let generated_records = sign_sorted_zone_records(
&apex,
RecordsIter::new_from_owned(&records),
&keys,
&GenerateRrsigConfig::new(
TEST_INCEPTION.into(),
TEST_EXPIRATION.into(),
),
)
.unwrap();
let mut iter = generated_records.iter();
assert_eq!(
*iter.next().unwrap(),
mk_rrsig_rr("example.", Rtype::NS, 1, "example.", &dnskey)
);
assert_eq!(
*iter.next().unwrap(),
mk_rrsig_rr("example.", Rtype::SOA, 1, "example.", &dnskey)
);
assert_eq!(
*iter.next().unwrap(),
mk_rrsig_rr("example.", Rtype::NSEC, 1, "example.", &dnskey)
);
assert_eq!(
*iter.next().unwrap(),
mk_rrsig_rr("ns.example.", Rtype::A, 2, "example.", &dnskey)
);
assert_eq!(
*iter.next().unwrap(),
mk_rrsig_rr("ns.example.", Rtype::NSEC, 2, "example.", &dnskey)
);
assert!(iter.next().is_none());
}
fn mk_dnssec_signing_key(make_ksk: bool) -> SigningKey<Bytes, TestKey> {
let flags = match make_ksk {
true => 257,
false => 256,
};
SigningKey::new(
Name::from_str("example").unwrap(),
flags,
TestKey::default(),
)
}
fn mk_dnskey_rr<R>(
name: &str,
dnskey: &Dnskey<Bytes>,
) -> Record<StoredName, R>
where
R: From<Dnskey<Bytes>>,
{
test_util::mk_dnskey_rr(
name,
dnskey.flags(),
dnskey.algorithm(),
dnskey.public_key(),
)
}
fn mk_rrsig_rr<R>(
name: &str,
covered_rtype: Rtype,
labels: u8,
signer_name: &str,
dnskey: &Dnskey<Bytes>,
) -> Record<StoredName, R>
where
R: From<Rrsig<Bytes, StoredName>>,
{
test_util::mk_rrsig_rr(
name,
covered_rtype,
&dnskey.algorithm(),
labels,
TEST_EXPIRATION,
TEST_INCEPTION,
dnskey.key_tag(),
signer_name,
TEST_SIGNATURE,
)
}
const TEST_SIGNATURE_RAW: [u8; 64] = [0u8; 64];
const TEST_SIGNATURE: Bytes = Bytes::from_static(&TEST_SIGNATURE_RAW);
#[derive(Debug)]
struct TestKey([u8; 32]);
impl SignRaw for TestKey {
fn algorithm(&self) -> SecurityAlgorithm {
SecurityAlgorithm::ED25519
}
fn dnskey(&self) -> Dnskey<Vec<u8>> {
let flags = 0;
Dnskey::new(flags, 3, SecurityAlgorithm::ED25519, self.0.to_vec())
.unwrap()
}
fn sign_raw(&self, _data: &[u8]) -> Result<Signature, SignError> {
Ok(Signature::Ed25519(TEST_SIGNATURE_RAW.into()))
}
}
impl Default for TestKey {
fn default() -> Self {
Self(rand::rng().random())
}
}
}