package dns import ( "crypto/sha1" "hash" "strings" ) type saltWireFmt struct { Salt string `dns:"size-hex"` } // HashName hashes a string (label) according to RFC 5155. It returns the hashed string in uppercase. func HashName(label string, ha uint8, iter uint16, salt string) string { saltwire := new(saltWireFmt) saltwire.Salt = salt wire := make([]byte, DefaultMsgSize) n, err := packSaltWire(saltwire, wire) if err != nil { return "" } wire = wire[:n] name := make([]byte, 255) off, err := PackDomainName(strings.ToLower(label), name, 0, nil, false) if err != nil { return "" } name = name[:off] var s hash.Hash switch ha { case SHA1: s = sha1.New() default: return "" } // k = 0 s.Write(name) s.Write(wire) nsec3 := s.Sum(nil) // k > 0 for k := uint16(0); k < iter; k++ { s.Reset() s.Write(nsec3) s.Write(wire) nsec3 = s.Sum(nsec3[:0]) } return toBase32(nsec3) } // Denialer is an interface that should be implemented by types that are used to denial // answers in DNSSEC. type Denialer interface { // Cover will check if the (unhashed) name is being covered by this NSEC or NSEC3. Cover(name string) bool // Match will check if the ownername matches the (unhashed) name for this NSEC3 or NSEC3. Match(name string) bool } // Cover implements the Denialer interface. func (rr *NSEC) Cover(name string) bool { return true } // Match implements the Denialer interface. func (rr *NSEC) Match(name string) bool { return true } // Cover implements the Denialer interface. func (rr *NSEC3) Cover(name string) bool { // FIXME(miek): check if the zones match // FIXME(miek): check if we're not dealing with parent nsec3 hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt) labels := Split(rr.Hdr.Name) if len(labels) < 2 { return false } hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the dot if hash == rr.NextDomain { return false // empty interval } if hash > rr.NextDomain { // last name, points to apex // hname > hash // hname > rr.NextDomain // TODO(miek) } if hname <= hash { return false } if hname >= rr.NextDomain { return false } return true } // Match implements the Denialer interface. func (rr *NSEC3) Match(name string) bool { // FIXME(miek): Check if we are in the same zone hname := HashName(name, rr.Hash, rr.Iterations, rr.Salt) labels := Split(rr.Hdr.Name) if len(labels) < 2 { return false } hash := strings.ToUpper(rr.Hdr.Name[labels[0] : labels[1]-1]) // -1 to remove the . if hash == hname { return true } return false } func packSaltWire(sw *saltWireFmt, msg []byte) (int, error) { off, err := packStringHex(sw.Salt, msg, 0) if err != nil { return off, err } return off, nil }