///|
/// Cryptographically authenticated under an explicitly trusted supplied key.
/// Private fields prevent constructing an authenticated proof without verification.
pub struct AuthenticatedSet {
priv records : Array[Record]
priv zone : Name
priv verified_at : UInt32
priv lifetime : UInt32
priv wildcard : Bool
} derive(Debug)
///|
pub extend AuthenticatedSet with @debug.Debug::{to_repr}
///|
pub fn AuthenticatedSet::records(self : AuthenticatedSet) -> Array[Record] {
self.records.copy()
}
///|
pub fn AuthenticatedSet::owner(self : AuthenticatedSet) -> Name {
self.records[0].owner
}
///|
pub fn AuthenticatedSet::rrtype(self : AuthenticatedSet) -> Int32 {
self.records[0].rrtype
}
///|
pub fn AuthenticatedSet::zone(self : AuthenticatedSet) -> Name {
self.zone
}
///|
pub fn AuthenticatedSet::lifetime(self : AuthenticatedSet) -> UInt32 {
self.lifetime
}
///|
/// A supplied key is a trust assertion by the caller; DNS presence is not trust.
pub fn authenticate_with_key(
records : Array[Record],
signatures : Array[Rrsig],
zone : Name,
key : Dnskey,
time : TimePolicy,
limits : Limits,
) -> Result[AuthenticatedSet, DnsError] {
match limits.validate() {
Ok(_) => ()
Err(e) => return Err(e)
}
match time.validate() {
Ok(_) => ()
Err(e) => return Err(e)
}
if signatures.is_empty() {
return Err(Indeterminate("no signatures"))
}
if signatures.length() > limits.max_signatures {
return Err(ResourceLimit("signature count"))
}
let tag = match key.key_tag(zone) {
Ok(n) => n
Err(e) => return Err(e)
}
let mut failure : DnsError = Indeterminate("no signature matches trusted key")
for sig in signatures {
if sig.signer != zone ||
sig.key_tag != tag ||
sig.algorithm != key.algorithm {
continue
}
let remaining = match sig.remaining(time) {
Ok(n) => n
Err(e) => {
failure = e
continue
}
}
let input = match signing_input(records, sig, limits) {
Ok(b) => b
Err(e) => {
failure = e
continue
}
}
match verify_signature(key, input, sig.signature) {
Err(e) => failure = e
Ok(false) => failure = Bogus("RRSIG cryptographic verification failed")
Ok(true) => {
let mut lifetime = remaining
if sig.original_ttl < lifetime {
lifetime = sig.original_ttl
}
for rr in records {
if rr.ttl < lifetime {
lifetime = rr.ttl
}
}
return Ok({
records: records.copy(),
zone,
verified_at: time.now,
lifetime,
wildcard: sig.labels < records[0].owner.label_count(),
})
}
}
}
Err(failure)
}