///|
pub struct Nsec3 {
priv owner_hash : Bytes
priv next_hash : Bytes
priv salt : Bytes
priv iterations : Int32
priv opt_out : Bool
priv bitmap : TypeBitmap
} derive(Debug)
///|
pub extend Nsec3 with @debug.Debug::{to_repr}
///|
/// Decode bounded SHA1 NSEC3 and enforce its immediate signer-zone owner.
pub fn Nsec3::parse(
record : Record,
zone : Name,
limits : Limits,
) -> Result[Nsec3, DnsError] {
match limits.validate() {
Ok(_) => ()
Err(e) => return Err(e)
}
if record.rrtype != 50 {
return Err(Malformed("expected NSEC3"))
}
if !record.owner.is_subdomain(zone) ||
record.owner.label_count() != zone.label_count() + 1 {
return Err(Bogus("NSEC3 owner must be hash directly below signer zone"))
}
let owner_hash = match decode_nsec3_label(record.owner.labels[0]) {
Ok(b) => b
Err(e) => return Err(e)
}
let d = record.data
if d.length() < 6 {
return Err(Malformed("short NSEC3 header"))
}
if d[0] != 1 {
return Err(Unsupported("only NSEC3 SHA1 is enabled"))
}
if d[1].to_int() > 1 {
return Err(Malformed("NSEC3 reserved flags"))
}
let iterations = u16_at(d, 2)
if iterations > limits.max_nsec3_iterations {
return Err(ResourceLimit("NSEC3 proof iterations"))
}
let salt_length = d[4].to_int()
let p = 5 + salt_length
if p >= d.length() || d[p] != 20 || d.length() - p - 1 < 20 {
return Err(Malformed("NSEC3 salt or next hash length"))
}
let bitmap = match TypeBitmap::parse(d[p + 21:].to_owned()) {
Ok(b) => b
Err(e) => return Err(e)
}
Ok({
owner_hash,
next_hash: d[p + 1:p + 21].to_owned(),
salt: d[5:p].to_owned(),
iterations,
opt_out: d[1] == 1,
bitmap,
})
}
///|
pub fn Nsec3::covers_hash(self : Nsec3, hash : Bytes) -> Bool {
if hash.length() != 20 {
return false
}
let a = bytes_compare(self.owner_hash, hash)
let b = bytes_compare(hash, self.next_hash)
if bytes_compare(self.owner_hash, self.next_hash) < 0 {
a < 0 && b < 0
} else {
a < 0 || b < 0
}
}
///|
pub fn nsec3_nodata(
proof : AuthenticatedSet,
zone : Name,
name : Name,
rrtype : Int32,
now : UInt32,
limits : Limits,
) -> Result[Denial, DnsError] {
match valid_query(zone, name, rrtype) {
Ok(_) => ()
Err(e) => return Err(e)
}
let record = match ensure_proof(proof, zone, 50, now) {
Ok(r) => r
Err(e) => return Err(e)
}
let n = match Nsec3::parse(record, zone, limits) {
Ok(n) => n
Err(e) => return Err(e)
}
let hash = match nsec3_hash(name, n.salt, n.iterations, limits) {
Ok(b) => b
Err(e) => return Err(e)
}
if n.owner_hash != hash {
return Err(Indeterminate("exact-name NSEC3 needed for NODATA"))
}
let kind = match absence_kind(n.bitmap, rrtype, name == zone) {
Ok(k) => k
Err(e) => return Err(e)
}
let lifetime = match proof.remaining(now) {
Ok(t) => t
Err(e) => return Err(e)
}
Ok({ kind, lifetime, })
}