package dns

import (
	
	
	
	
	
	
	
	_    // need its init function
	_  // need its init function
	_  // need its init function
	
	
	
	
	
	
	
)

// DNSSEC encryption algorithm codes.
const (
	_ uint8 = iota
	RSAMD5
	DH
	DSA
	_ // Skip 4, RFC 6725, section 2.1
	RSASHA1
	DSANSEC3SHA1
	RSASHA1NSEC3SHA1
	RSASHA256
	_ // Skip 9, RFC 6725, section 2.1
	RSASHA512
	_ // Skip 11, RFC 6725, section 2.1
	ECCGOST
	ECDSAP256SHA256
	ECDSAP384SHA384
	ED25519
	ED448
	INDIRECT   uint8 = 252
	PRIVATEDNS uint8 = 253 // Private (experimental keys)
	PRIVATEOID uint8 = 254
)

// AlgorithmToString is a map of algorithm IDs to algorithm names.
var AlgorithmToString = map[uint8]string{
	RSAMD5:           "RSAMD5",
	DH:               "DH",
	DSA:              "DSA",
	RSASHA1:          "RSASHA1",
	DSANSEC3SHA1:     "DSA-NSEC3-SHA1",
	RSASHA1NSEC3SHA1: "RSASHA1-NSEC3-SHA1",
	RSASHA256:        "RSASHA256",
	RSASHA512:        "RSASHA512",
	ECCGOST:          "ECC-GOST",
	ECDSAP256SHA256:  "ECDSAP256SHA256",
	ECDSAP384SHA384:  "ECDSAP384SHA384",
	ED25519:          "ED25519",
	ED448:            "ED448",
	INDIRECT:         "INDIRECT",
	PRIVATEDNS:       "PRIVATEDNS",
	PRIVATEOID:       "PRIVATEOID",
}

// AlgorithmToHash is a map of algorithm crypto hash IDs to crypto.Hash's.
// For newer algorithm that do their own hashing (i.e. ED25519) the returned value
// is 0, implying no (external) hashing should occur. The non-exported identityHash is then
// used.
var AlgorithmToHash = map[uint8]crypto.Hash{
	RSAMD5:           crypto.MD5, // Deprecated in RFC 6725
	DSA:              crypto.SHA1,
	RSASHA1:          crypto.SHA1,
	RSASHA1NSEC3SHA1: crypto.SHA1,
	RSASHA256:        crypto.SHA256,
	ECDSAP256SHA256:  crypto.SHA256,
	ECDSAP384SHA384:  crypto.SHA384,
	RSASHA512:        crypto.SHA512,
	ED25519:          0,
}

// DNSSEC hashing algorithm codes.
const (
	_      uint8 = iota
	SHA1         // RFC 4034
	SHA256       // RFC 4509
	GOST94       // RFC 5933
	SHA384       // Experimental
	SHA512       // Experimental
)

// HashToString is a map of hash IDs to names.
var HashToString = map[uint8]string{
	SHA1:   "SHA1",
	SHA256: "SHA256",
	GOST94: "GOST94",
	SHA384: "SHA384",
	SHA512: "SHA512",
}

// DNSKEY flag values.
const (
	SEP    = 1
	REVOKE = 1 << 7
	ZONE   = 1 << 8
)

// The RRSIG needs to be converted to wireformat with some of the rdata (the signature) missing.
type rrsigWireFmt struct {
	TypeCovered uint16
	Algorithm   uint8
	Labels      uint8
	OrigTtl     uint32
	Expiration  uint32
	Inception   uint32
	KeyTag      uint16
	SignerName  string `dns:"domain-name"`
	/* No Signature */
}

// Used for converting DNSKEY's rdata to wirefmt.
type dnskeyWireFmt struct {
	Flags     uint16
	Protocol  uint8
	Algorithm uint8
	PublicKey string `dns:"base64"`
	/* Nothing is left out */
}

// KeyTag calculates the keytag (or key-id) of the DNSKEY.
func ( *DNSKEY) () uint16 {
	if  == nil {
		return 0
	}
	var  int
	switch .Algorithm {
	case RSAMD5:
		// This algorithm has been deprecated, but keep this key-tag calculation.
		// Look at the bottom two bytes of the modules, which the last item in the pubkey.
		// See https://www.rfc-editor.org/errata/eid193 .
		,  := fromBase64([]byte(.PublicKey))
		if len() > 1 {
			 := binary.BigEndian.Uint16([len()-3:])
			 = int()
		}
	default:
		 := new(dnskeyWireFmt)
		.Flags = .Flags
		.Protocol = .Protocol
		.Algorithm = .Algorithm
		.PublicKey = .PublicKey
		 := make([]byte, DefaultMsgSize)
		,  := packKeyWire(, )
		if  != nil {
			return 0
		}
		 = [:]
		for ,  := range  {
			if &1 != 0 {
				 += int() // must be larger than uint32
			} else {
				 += int() << 8
			}
		}
		 +=  >> 16 & 0xFFFF
		 &= 0xFFFF
	}
	return uint16()
}

// ToDS converts a DNSKEY record to a DS record.
func ( *DNSKEY) ( uint8) *DS {
	if  == nil {
		return nil
	}
	 := new(DS)
	.Hdr.Name = .Hdr.Name
	.Hdr.Class = .Hdr.Class
	.Hdr.Rrtype = TypeDS
	.Hdr.Ttl = .Hdr.Ttl
	.Algorithm = .Algorithm
	.DigestType = 
	.KeyTag = .KeyTag()

	 := new(dnskeyWireFmt)
	.Flags = .Flags
	.Protocol = .Protocol
	.Algorithm = .Algorithm
	.PublicKey = .PublicKey
	 := make([]byte, DefaultMsgSize)
	,  := packKeyWire(, )
	if  != nil {
		return nil
	}
	 = [:]

	 := make([]byte, 255)
	,  := PackDomainName(CanonicalName(.Hdr.Name), , 0, nil, false)
	if  != nil {
		return nil
	}
	 = [:]
	// RFC4034:
	// digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
	// "|" denotes concatenation
	// DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.

	var  crypto.Hash
	switch  {
	case SHA1:
		 = crypto.SHA1
	case SHA256:
		 = crypto.SHA256
	case SHA384:
		 = crypto.SHA384
	case SHA512:
		 = crypto.SHA512
	default:
		return nil
	}

	 := .New()
	.Write()
	.Write()
	.Digest = hex.EncodeToString(.Sum(nil))
	return 
}

// ToCDNSKEY converts a DNSKEY record to a CDNSKEY record.
func ( *DNSKEY) () *CDNSKEY {
	 := &CDNSKEY{DNSKEY: *}
	.Hdr = .Hdr
	.Hdr.Rrtype = TypeCDNSKEY
	return 
}

// ToCDS converts a DS record to a CDS record.
func ( *DS) () *CDS {
	 := &CDS{DS: *}
	.Hdr = .Hdr
	.Hdr.Rrtype = TypeCDS
	return 
}

// Sign signs an RRSet. The signature needs to be filled in with the values:
// Inception, Expiration, KeyTag, SignerName and Algorithm.  The rest is copied
// from the RRset. Sign returns a non-nill error when the signing went OK.
// There is no check if RRSet is a proper (RFC 2181) RRSet.  If OrigTTL is non
// zero, it is used as-is, otherwise the TTL of the RRset is used as the
// OrigTTL.
func ( *RRSIG) ( crypto.Signer,  []RR) error {
	 := [0].Header()
	.Hdr.Rrtype = TypeRRSIG
	.Hdr.Name = .Name
	.Hdr.Class = .Class
	if .OrigTtl == 0 { // If set don't override
		.OrigTtl = .Ttl
	}
	.TypeCovered = .Rrtype
	.Labels = uint8(CountLabel(.Name))

	if strings.HasPrefix(.Name, "*") {
		.Labels-- // wildcard, remove from label count
	}

	return .signAsIs(, )
}

func ( *RRSIG) ( crypto.Signer,  []RR) error {
	if  == nil {
		return ErrPrivKey
	}
	// s.Inception and s.Expiration may be 0 (rollover etc.), the rest must be set
	if .KeyTag == 0 || len(.SignerName) == 0 || .Algorithm == 0 {
		return ErrKey
	}

	 := new(rrsigWireFmt)
	.TypeCovered = .TypeCovered
	.Algorithm = .Algorithm
	.Labels = .Labels
	.OrigTtl = .OrigTtl
	.Expiration = .Expiration
	.Inception = .Inception
	.KeyTag = .KeyTag
	// For signing, lowercase this name
	.SignerName = CanonicalName(.SignerName)

	// Create the desired binary blob
	 := make([]byte, DefaultMsgSize)
	,  := packSigWire(, )
	if  != nil {
		return 
	}
	 = [:]
	,  := rawSignatureData(, )
	if  != nil {
		return 
	}

	, ,  := hashFromAlgorithm(.Algorithm)
	if  != nil {
		return 
	}

	switch .Algorithm {
	case RSAMD5, DSA, DSANSEC3SHA1:
		// See RFC 6944.
		return ErrAlg
	default:
		.Write()
		.Write()

		,  := sign(, .Sum(nil), , .Algorithm)
		if  != nil {
			return 
		}

		.Signature = toBase64()
		return nil
	}
}

func sign( crypto.Signer,  []byte,  crypto.Hash,  uint8) ([]byte, error) {
	,  := .Sign(rand.Reader, , )
	if  != nil {
		return nil, 
	}

	switch  {
	case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512, ED25519:
		return , nil
	case ECDSAP256SHA256, ECDSAP384SHA384:
		 := &struct {
			,  *big.Int
		}{}
		if ,  := asn1.Unmarshal(, );  != nil {
			return nil, 
		}

		var  int
		switch  {
		case ECDSAP256SHA256:
			 = 32
		case ECDSAP384SHA384:
			 = 48
		}

		 := intToBytes(., )
		 = append(, intToBytes(., )...)
		return , nil
	default:
		return nil, ErrAlg
	}
}

// Verify validates an RRSet with the signature and key. This is only the
// cryptographic test, the signature validity period must be checked separately.
// This function copies the rdata of some RRs (to lowercase domain names) for the validation to work.
// It also checks that the Zone Key bit (RFC 4034 2.1.1) is set on the DNSKEY
// and that the Protocol field is set to 3 (RFC 4034 2.1.2).
func ( *RRSIG) ( *DNSKEY,  []RR) error {
	// First the easy checks
	if !IsRRset() {
		return ErrRRset
	}
	if .KeyTag != .KeyTag() {
		return ErrKey
	}
	if .Hdr.Class != .Hdr.Class {
		return ErrKey
	}
	if .Algorithm != .Algorithm {
		return ErrKey
	}

	 := CanonicalName(.SignerName)
	if !equal(, .Hdr.Name) {
		return ErrKey
	}

	if .Protocol != 3 {
		return ErrKey
	}
	// RFC 4034 2.1.1 If bit 7 has value 0, then the DNSKEY record holds some
	// other type of DNS public key and MUST NOT be used to verify RRSIGs that
	// cover RRsets.
	if .Flags&ZONE == 0 {
		return ErrKey
	}

	// IsRRset checked that we have at least one RR and that the RRs in
	// the set have consistent type, class, and name. Also check that type,
	// class and name matches the RRSIG record.
	// Also checks RFC 4035 5.3.1 the number of labels in the RRset owner
	// name MUST be greater than or equal to the value in the RRSIG RR's Labels field.
	// RFC 4035 5.3.1 Signer's Name MUST be the name of the zone that [contains the RRset].
	// Since we don't have SOA info, checking suffix may be the best we can do...?
	if  := [0].Header(); .Class != .Hdr.Class ||
		.Rrtype != .TypeCovered ||
		uint8(CountLabel(.Name)) < .Labels ||
		!equal(.Name, .Hdr.Name) ||
		!strings.HasSuffix(CanonicalName(.Name), ) {

		return ErrRRset
	}

	// RFC 4035 5.3.2.  Reconstructing the Signed Data
	// Copy the sig, except the rrsig data
	 := new(rrsigWireFmt)
	.TypeCovered = .TypeCovered
	.Algorithm = .Algorithm
	.Labels = .Labels
	.OrigTtl = .OrigTtl
	.Expiration = .Expiration
	.Inception = .Inception
	.KeyTag = .KeyTag
	.SignerName = 
	// Create the desired binary blob
	 := make([]byte, DefaultMsgSize)
	,  := packSigWire(, )
	if  != nil {
		return 
	}
	 = [:]
	,  := rawSignatureData(, )
	if  != nil {
		return 
	}

	 := .sigBuf() // Get the binary signature data
	// TODO(miek)
	// remove the domain name and assume its ours?
	// if rr.Algorithm == PRIVATEDNS { // PRIVATEOID
	// }

	, ,  := hashFromAlgorithm(.Algorithm)
	if  != nil {
		return 
	}

	switch .Algorithm {
	case RSASHA1, RSASHA1NSEC3SHA1, RSASHA256, RSASHA512:
		// TODO(mg): this can be done quicker, ie. cache the pubkey data somewhere??
		 := .publicKeyRSA() // Get the key
		if  == nil {
			return ErrKey
		}

		.Write()
		.Write()
		return rsa.VerifyPKCS1v15(, , .Sum(nil), )

	case ECDSAP256SHA256, ECDSAP384SHA384:
		 := .publicKeyECDSA()
		if  == nil {
			return ErrKey
		}

		// Split sigbuf into the r and s coordinates
		 := new(big.Int).SetBytes([:len()/2])
		 := new(big.Int).SetBytes([len()/2:])

		.Write()
		.Write()
		if ecdsa.Verify(, .Sum(nil), , ) {
			return nil
		}
		return ErrSig

	case ED25519:
		 := .publicKeyED25519()
		if  == nil {
			return ErrKey
		}

		if ed25519.Verify(, append(, ...), ) {
			return nil
		}
		return ErrSig

	default:
		return ErrAlg
	}
}

// ValidityPeriod uses RFC1982 serial arithmetic to calculate
// if a signature period is valid. If t is the zero time, the
// current time is taken other t is. Returns true if the signature
// is valid at the given time, otherwise returns false.
func ( *RRSIG) ( time.Time) bool {
	var  int64
	if .IsZero() {
		 = time.Now().UTC().Unix()
	} else {
		 = .UTC().Unix()
	}
	 := (int64(.Inception) - ) / year68
	 := (int64(.Expiration) - ) / year68
	 := int64(.Inception) + *year68
	 := int64(.Expiration) + *year68
	return  <=  &&  <= 
}

// Return the signatures base64 encoding sigdata as a byte slice.
func ( *RRSIG) () []byte {
	,  := fromBase64([]byte(.Signature))
	if  != nil {
		return nil
	}
	return 
}

// publicKeyRSA returns the RSA public key from a DNSKEY record.
func ( *DNSKEY) () *rsa.PublicKey {
	,  := fromBase64([]byte(.PublicKey))
	if  != nil {
		return nil
	}

	if len() < 1+1+64 {
		// Exponent must be at least 1 byte and modulus at least 64
		return nil
	}

	// RFC 2537/3110, section 2. RSA Public KEY Resource Records
	// Length is in the 0th byte, unless its zero, then it
	// it in bytes 1 and 2 and its a 16 bit number
	 := uint16([0])
	 := 1
	if  == 0 {
		 = uint16([1])<<8 | uint16([2])
		 = 3
	}

	if  > 4 ||  == 0 || [] == 0 {
		// Exponent larger than supported by the crypto package,
		// empty, or contains prohibited leading zero.
		return nil
	}

	 :=  + int()
	 := len() - 
	if  < 64 ||  > 512 || [] == 0 {
		// Modulus is too small, large, or contains prohibited leading zero.
		return nil
	}

	 := new(rsa.PublicKey)

	var  uint64
	// The exponent of length explen is between keyoff and modoff.
	for ,  := range [:] {
		 <<= 8
		 |= uint64()
	}
	if  > 1<<31-1 {
		// Larger exponent than supported by the crypto package.
		return nil
	}

	.E = int()
	.N = new(big.Int).SetBytes([:])
	return 
}

// publicKeyECDSA returns the Curve public key from the DNSKEY record.
func ( *DNSKEY) () *ecdsa.PublicKey {
	,  := fromBase64([]byte(.PublicKey))
	if  != nil {
		return nil
	}
	 := new(ecdsa.PublicKey)
	switch .Algorithm {
	case ECDSAP256SHA256:
		.Curve = elliptic.P256()
		if len() != 64 {
			// wrongly encoded key
			return nil
		}
	case ECDSAP384SHA384:
		.Curve = elliptic.P384()
		if len() != 96 {
			// Wrongly encoded key
			return nil
		}
	}
	.X = new(big.Int).SetBytes([:len()/2])
	.Y = new(big.Int).SetBytes([len()/2:])
	return 
}

func ( *DNSKEY) () ed25519.PublicKey {
	,  := fromBase64([]byte(.PublicKey))
	if  != nil {
		return nil
	}
	if len() != ed25519.PublicKeySize {
		return nil
	}
	return 
}

type wireSlice [][]byte

func ( wireSlice) () int      { return len() }
func ( wireSlice) (,  int) { [], [] = [], [] }
func ( wireSlice) (,  int) bool {
	, ,  := UnpackDomainName([], 0)
	, ,  := UnpackDomainName([], 0)
	return bytes.Compare([][+10:], [][+10:]) < 0
}

// Return the raw signature data.
func rawSignatureData( []RR,  *RRSIG) ( []byte,  error) {
	 := make(wireSlice, len())
	for ,  := range  {
		 := .copy()
		 := .Header()
		.Ttl = .OrigTtl
		 := SplitDomainName(.Name)
		// 6.2. Canonical RR Form. (4) - wildcards
		if len() > int(.Labels) {
			// Wildcard
			.Name = "*." + strings.Join([len()-int(.Labels):], ".") + "."
		}
		// RFC 4034: 6.2.  Canonical RR Form. (2) - domain name to lowercase
		.Name = CanonicalName(.Name)
		// 6.2. Canonical RR Form. (3) - domain rdata to lowercase.
		//   NS, MD, MF, CNAME, SOA, MB, MG, MR, PTR,
		//   HINFO, MINFO, MX, RP, AFSDB, RT, SIG, PX, NXT, NAPTR, KX,
		//   SRV, DNAME, A6
		//
		// RFC 6840 - Clarifications and Implementation Notes for DNS Security (DNSSEC):
		//	Section 6.2 of [RFC4034] also erroneously lists HINFO as a record
		//	that needs conversion to lowercase, and twice at that.  Since HINFO
		//	records contain no domain names, they are not subject to case
		//	conversion.
		switch x := .(type) {
		case *NS:
			.Ns = CanonicalName(.Ns)
		case *MD:
			.Md = CanonicalName(.Md)
		case *MF:
			.Mf = CanonicalName(.Mf)
		case *CNAME:
			.Target = CanonicalName(.Target)
		case *SOA:
			.Ns = CanonicalName(.Ns)
			.Mbox = CanonicalName(.Mbox)
		case *MB:
			.Mb = CanonicalName(.Mb)
		case *MG:
			.Mg = CanonicalName(.Mg)
		case *MR:
			.Mr = CanonicalName(.Mr)
		case *PTR:
			.Ptr = CanonicalName(.Ptr)
		case *MINFO:
			.Rmail = CanonicalName(.Rmail)
			.Email = CanonicalName(.Email)
		case *MX:
			.Mx = CanonicalName(.Mx)
		case *RP:
			.Mbox = CanonicalName(.Mbox)
			.Txt = CanonicalName(.Txt)
		case *AFSDB:
			.Hostname = CanonicalName(.Hostname)
		case *RT:
			.Host = CanonicalName(.Host)
		case *SIG:
			.SignerName = CanonicalName(.SignerName)
		case *PX:
			.Map822 = CanonicalName(.Map822)
			.Mapx400 = CanonicalName(.Mapx400)
		case *NAPTR:
			.Replacement = CanonicalName(.Replacement)
		case *KX:
			.Exchanger = CanonicalName(.Exchanger)
		case *SRV:
			.Target = CanonicalName(.Target)
		case *DNAME:
			.Target = CanonicalName(.Target)
		}
		// 6.2. Canonical RR Form. (5) - origTTL
		 := make([]byte, Len()+1) // +1 to be safe(r)
		,  := PackRR(, , 0, nil, false)
		if  != nil {
			return nil, 
		}
		 = [:]
		[] = 
	}
	sort.Sort()
	for ,  := range  {
		if  > 0 && bytes.Equal(, [-1]) {
			continue
		}
		 = append(, ...)
	}
	return , nil
}

func packSigWire( *rrsigWireFmt,  []byte) (int, error) {
	// copied from zmsg.go RRSIG packing
	,  := packUint16(.TypeCovered, , 0)
	if  != nil {
		return , 
	}
	,  = packUint8(.Algorithm, , )
	if  != nil {
		return , 
	}
	,  = packUint8(.Labels, , )
	if  != nil {
		return , 
	}
	,  = packUint32(.OrigTtl, , )
	if  != nil {
		return , 
	}
	,  = packUint32(.Expiration, , )
	if  != nil {
		return , 
	}
	,  = packUint32(.Inception, , )
	if  != nil {
		return , 
	}
	,  = packUint16(.KeyTag, , )
	if  != nil {
		return , 
	}
	,  = PackDomainName(.SignerName, , , nil, false)
	if  != nil {
		return , 
	}
	return , nil
}

func packKeyWire( *dnskeyWireFmt,  []byte) (int, error) {
	// copied from zmsg.go DNSKEY packing
	,  := packUint16(.Flags, , 0)
	if  != nil {
		return , 
	}
	,  = packUint8(.Protocol, , )
	if  != nil {
		return , 
	}
	,  = packUint8(.Algorithm, , )
	if  != nil {
		return , 
	}
	,  = packStringBase64(.PublicKey, , )
	if  != nil {
		return , 
	}
	return , nil
}