diff --git a/Makefile b/Makefile index d5710aef..262896bf 100644 --- a/Makefile +++ b/Makefile @@ -12,18 +12,17 @@ GOFILES=\ types.go\ edns.go\ tsig.go\ + dnssec.go\ include $(GOROOT)/src/Make.pkg all: package - gomake -C dnssec package gomake -C resolver package # gomake -C strconv package dnstest: gotest - gomake -C dnssec test gomake -C resolver test # gomake -C strconv test diff --git a/TODO b/TODO index 38550c6e..8c443a6f 100644 --- a/TODO +++ b/TODO @@ -9,12 +9,13 @@ Short term: * Signature generation Issues: +* Separation between dnssec and dns is arbitrary, why is tsig.go of package dns? * Better sized buffers * Check the network order, it works now, but this is on Intel * Make the testsuite work with public DNS servers -* shortened ipv6 addresses are not parsed correctly (maby net issue) +* shortened ipv6 addresses are not parsed correctly (maybe net issue) * quoted quotes in txt records -* Convience functions? +* Convenience functions? - for new(RR*) - nsupdate * query-time, server in string ouput of dns.Msg diff --git a/dnssec.go b/dnssec.go new file mode 100644 index 00000000..3ff07fbc --- /dev/null +++ b/dnssec.go @@ -0,0 +1,272 @@ +// Package dnssec implements all client side DNSSEC function, like +// validation, keytag and DS calculation. +package dns + +import ( + "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "crypto/rsa" + "encoding/hex" + "encoding/base64" + "hash" + "time" + "io" + "big" + "sort" + "strings" + "os" +) + +// DNSSEC encryption algorithm codes. +const ( + // DNSSEC algorithms + AlgRSAMD5 = 1 + AlgDH = 2 + AlgDSA = 3 + AlgECC = 4 + AlgRSASHA1 = 5 + AlgRSASHA256 = 8 + AlgRSASHA512 = 10 + AlgECCGOST = 12 +) + +// DNSSEC hashing codes. +const ( + HashSHA1 = iota + HashSHA256 + HashGOST94 +) + +// Calculate the keytag of the DNSKEY. +func (k *RR_DNSKEY) KeyTag() uint16 { + var keytag int + switch k.Algorithm { + case AlgRSAMD5: + println("Keytag RSAMD5. Todo") + keytag = 0 + default: + // Might encode header length too, so that + // we dont need to pack/unpack all the time + // Or a shadow structure, with the wiredata and header + wire, ok := WireRdata(k) + if !ok { + return 0 + } + for i, v := range wire { + if i&1 != 0 { + keytag += int(v) // must be larger than uint32 + } else { + keytag += int(v) << 8 + } + } + keytag += (keytag >> 16) & 0xFFFF + keytag &= 0xFFFF + } + return uint16(keytag) +} + +// Convert an DNSKEY record to a DS record. +func (k *RR_DNSKEY) ToDS(h int) *RR_DS { + ds := new(RR_DS) + ds.Hdr.Name = k.Hdr.Name + ds.Hdr.Class = k.Hdr.Class + ds.Hdr.Ttl = k.Hdr.Ttl + ds.Algorithm = k.Algorithm + ds.DigestType = uint8(h) + ds.KeyTag = k.KeyTag() + + wire, ok := WireRdata(k) + if !ok { + return nil + } + + owner, ok1 := WireDomainName(k.Hdr.Name) + if !ok1 { + return nil + } + /* + * from RFC4034 + * digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA); + * "|" denotes concatenation + * DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key. + */ + // digest buffer + digest := append(owner, wire...) // another copy TODO(mg) + + switch h { + case HashSHA1: + s := sha1.New() + io.WriteString(s, string(digest)) + ds.Digest = hex.EncodeToString(s.Sum()) + case HashSHA256: + s := sha256.New() + io.WriteString(s, string(digest)) + ds.Digest = hex.EncodeToString(s.Sum()) + case HashGOST94: + + default: + // wrong hash value + return nil + } + return ds +} + + +// Validate an rrset with the signature and key. This is the +// cryptographic test, the validity period most be check separately. +func (s *RR_RRSIG) Verify(k *RR_DNSKEY, rrset RRset) bool { + // Frist the easy checks + if s.KeyTag != k.KeyTag() { + println(s.KeyTag) + println(k.KeyTag()) + return false + } + if s.Hdr.Class != k.Hdr.Class { + println("Class") + return false + } + if s.Algorithm != k.Algorithm { + println("Class") + return false + } + if s.SignerName != k.Hdr.Name { + println(s.SignerName) + println(k.Hdr.Name) + return false + } + for _, r := range rrset { + if r.Header().Class != s.Hdr.Class { + return false + } + if r.Header().Rrtype != s.TypeCovered { + return false + } + // Number of labels. TODO(mg) add helper functions + } + sort.Sort(rrset) + + // RFC 4035 5.3.2. Reconstructing the Signed Data + // Copy the sig, except the rrsig data + s1 := &RR_RRSIG{s.Hdr, s.TypeCovered, s.Algorithm, s.Labels, s.OrigTtl, s.Expiration, s.Inception, s.KeyTag, s.SignerName, ""} + signeddata, ok := WireRdata(s1) + if !ok { + return false + } + + for _, r := range rrset { + h := r.Header() + // RFC 4034: 6.2. Canonical RR Form. (2) - domain name to lowercase + name := h.Name + h.Name = strings.ToLower(h.Name) + // 6.2. Canonical RR Form. (3) - domain rdata to lowercaser + switch h.Rrtype { + case TypeNS, TypeCNAME, TypeSOA, TypeMB, TypeMG, TypeMR, TypePTR: + case TypeHINFO, TypeMINFO, TypeMX /* TypeRP, TypeAFSDB, TypeRT */ : + case TypeSIG /* TypePX, TypeNXT /* TypeNAPTR, TypeKX */ : + case TypeSRV, /* TypeDNAME, TypeA6 */ TypeRRSIG, TypeNSEC: + // lower case the domain rdata // + + } + // 6.2. Canonical RR Form. (4) - wildcards, don't understand + // 6.2. Canonical RR Form. (5) - origTTL + ttl := h.Ttl + h.Ttl = s.OrigTtl + wire, ok1 := WireRR(r) + h.Ttl = ttl // restore the order in the universe + h.Name = name + if !ok1 { + println("Failure to pack") + return false + } + signeddata = append(signeddata, wire...) + } + + // Buffer holding the key data + keybuf := make([]byte, 1024) + keybuflen := base64.StdEncoding.DecodedLen(len(k.PubKey)) + keybuflen, _ = base64.StdEncoding.Decode(keybuf[0:keybuflen], []byte(k.PubKey)) + keybuf = keybuf[:keybuflen] + + // Buffer holding the signature + sigbuf := make([]byte, 1024) + sigbuflen := base64.StdEncoding.DecodedLen(len(s.Signature)) + sigbuflen, _ = base64.StdEncoding.Decode(sigbuf[0:sigbuflen], []byte(s.Signature)) + sigbuf = sigbuf[:sigbuflen] + + var err os.Error + switch s.Algorithm { + case AlgRSASHA1, AlgRSASHA256, AlgRSASHA512, AlgRSAMD5: + pubkey := rsaPubKey(keybuf) + // Setup the hash as defined for this alg. + var h hash.Hash + var ch rsa.PKCS1v15Hash + switch s.Algorithm { + case AlgRSAMD5: + h = md5.New() + ch = rsa.HashMD5 + case AlgRSASHA1: + h = sha1.New() + ch = rsa.HashSHA1 + case AlgRSASHA256: + h = sha256.New() + ch = rsa.HashSHA256 + case AlgRSASHA512: + h = sha512.New() + ch = rsa.HashSHA512 + } + io.WriteString(h, string(signeddata)) + sighash := h.Sum() + err = rsa.VerifyPKCS1v15(pubkey, ch, sighash, sigbuf) + case AlgDH: + case AlgDSA: + case AlgECC: + case AlgECCGOST: + } + + return err == nil +} + +// Using RFC1982 calculate if a signature period is valid +func (s *RR_RRSIG) PeriodOK() bool { + utc := time.UTC().Seconds() + modi := (int64(s.Inception) - utc) / Year68 + mode := (int64(s.Expiration) - utc) / Year68 + ti := int64(s.Inception) + (modi * Year68) + te := int64(s.Expiration) + (mode * Year68) + return ti <= utc && utc <= te +} + +// Extra the RSA public key from the buffer +func rsaPubKey(keybuf []byte) *rsa.PublicKey { + // 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 + explen := uint16(keybuf[0]) + keyoff := 1 + if explen == 0 { + explen = uint16(keybuf[1])<<8 | uint16(keybuf[2]) + keyoff = 3 + } + pubkey := new(rsa.PublicKey) + pubkey.N = big.NewInt(0) + shift := (explen - 1) * 8 + for i := int(explen - 1); i >= 0; i-- { + pubkey.E += int(keybuf[keyoff+i]) << shift + shift -= 8 + } + pubkey.N.SetBytes(keybuf[keyoff+int(explen):]) + return pubkey +} + +// Map for algorithm names. +var alg_str = map[uint8]string{ + AlgRSAMD5: "RSAMD5", + AlgDH: "DH", + AlgDSA: "DSA", + AlgRSASHA1: "RSASHA1", + AlgRSASHA256: "RSASHA256", + AlgRSASHA512: "RSASHA512", + AlgECCGOST: "ECC-GOST", +} diff --git a/dnssec/dnskey_test.go b/dnssec/dnskey_test.go deleted file mode 100644 index a2bdf8b3..00000000 --- a/dnssec/dnskey_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package dnssec - -import ( - "testing" - "fmt" - "dns" -) - -func TestDnskey(t *testing.T) { - return - // This key was generate with LDNS: - // ldns-keygen -a RSASHA256 -r /dev/urandom -b 1024 miek.nl - // Show that we have al the RSA parameters and can check them - // here to see what I came up with - key := new(dns.RR_DNSKEY) - key.Hdr.Name = "miek.nl." - key.Hdr.Rrtype = dns.TypeDNSKEY - key.Hdr.Class = dns.ClassINET - key.Hdr.Ttl = 3600 - key.Flags = 256 - key.Protocol = 3 - key.Algorithm = AlgRSASHA256 - key.PubKey = "AwEAAcELcuxHosJX3LjbR6EFzsqI3mKivwvO6Y5Kzt/OXYmLQUI8tnOrX9ilT/0qGraxoONayVX3A6bl1pG3h/xOxVEGcJGqbrZnhr2+4S9tW2GWQwevV+NhinE7v6MCCCheVCnAPh0KFb/u14ng3DQizP1spBU/NoAN31l678snBpZX" - fmt.Printf("%v\n", key) - - soa := new(dns.RR_SOA) - soa.Hdr = dns.RR_Header{"Miek.nl.", dns.TypeSOA, dns.ClassINET, 875, 0} - soa.Ns = "open.nlnetlabs.nl." - soa.Mbox = "miekg.atoom.net." - soa.Serial = 1293513905 - soa.Refresh = 14400 - soa.Retry = 3600 - soa.Expire = 604800 - soa.Minttl = 86400 - - sig := new(dns.RR_RRSIG) - sig.Hdr = dns.RR_Header{"miek.nl.", dns.TypeRRSIG, dns.ClassINET, 14400, 0} - sig.TypeCovered = dns.TypeSOA - sig.Algorithm = AlgRSASHA256 - sig.Labels = 2 - sig.Expiration = 1296098705 // date '+%s' -d"2011-01-27 04:25:05 - sig.Inception = 1293506705 - sig.OrigTtl = 14400 - //sig.KeyTag = 12051 - sig.KeyTag = 12273 //faked - sig.SignerName = "miek.nl." - sig.Signature = "kLq/5oFy3Sh5ZxPGFMCyHq8MtN6E17R1Ln9+bJ2Q76YYAxFE8Xlie33A1GFctH2uhzRzJKuP/JSjUkrvGk2rjBm32z9zXtZsKx/4yV0da2nLRm44NOmX6gsP4Yia8mdqPUajjkyLzAzU2bevtesJm0Z65AcmPdq3tUZODdRAcng=" - - Verify(sig, key, []dns.RR{soa}) - - // From Kmiek.nl*.private - openssl := "135560614087352210480379313279722604826647214111257577861451621491284835543707521986085999189597017237768514876957888744370440811423088511394629855684615382349190289731989185193184712980579812986523080792122141528583964882610028199770199112837017606561901919812183422914622295620927795008308854924436086101591" - println("OPENSSL key: what should be is: ",openssl) -} diff --git a/dnssec/dnssec.go b/dnssec/dnssec.go index 4fd44777..83133b11 100644 --- a/dnssec/dnssec.go +++ b/dnssec/dnssec.go @@ -2,8 +2,6 @@ // validation, keytag and DS calculation. package dnssec -// Put tsig and tkey stuff here too - import ( "crypto/md5" "crypto/sha1" @@ -244,7 +242,7 @@ func PeriodOK(s *dns.RR_RRSIG) bool { func rsaPubKey(keybuf []byte) *rsa.PublicKey { // 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 it a 16 bit number + // it in bytes 1 and 2 and its a 16 bit number explen := uint16(keybuf[0]) keyoff := 1 if explen == 0 { diff --git a/dnssec/dnssec_test.go b/dnssec/dnssec_test.go deleted file mode 100644 index fb1a9e94..00000000 --- a/dnssec/dnssec_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package dnssec - -import ( - "testing" - "fmt" - "os" - "dns" -) - -func TestSecure(t *testing.T) { -// once this was valid - soa := new(dns.RR_SOA) - soa.Hdr = dns.RR_Header{"miek.nl.", dns.TypeSOA, dns.ClassINET, 14400, 0} - soa.Ns = "open.nlnetlabs.nl." - soa.Mbox = "miekg.atoom.net." - soa.Serial = 1293945905 - soa.Refresh = 14400 - soa.Retry = 3600 - soa.Expire = 604800 - soa.Minttl = 86400 - - sig := new(dns.RR_RRSIG) - sig.Hdr = dns.RR_Header{"miek.nl.", dns.TypeRRSIG, dns.ClassINET, 14400, 0} - sig.TypeCovered = dns.TypeSOA - sig.Algorithm = AlgRSASHA256 - sig.Labels = 2 - // UTC LUL! - sig.Expiration = 1296534305 // date -u '+%s' -d"2011-02-01 04:25:05" - sig.Inception = 1293942305 // date -u '+%s' -d"2011-01-02 04:25:05" - sig.OrigTtl = 14400 - sig.KeyTag = 12051 - sig.SignerName = "miek.nl." - sig.Signature = "oMCbslaAVIp/8kVtLSms3tDABpcPRUgHLrOR48OOplkYo+8TeEGWwkSwaz/MRo2fB4FxW0qj/hTlIjUGuACSd+b1wKdH5GvzRJc2pFmxtCbm55ygAh4EUL0F6U5cKtGJGSXxxg6UFCQ0doJCmiGFa78LolaUOXImJrk6AFrGa0M=" - - key := new(dns.RR_DNSKEY) - key.Hdr.Name = "miek.nl." - key.Hdr.Class = dns.ClassINET - key.Hdr.Ttl = 14400 - key.Flags = 256 - key.Protocol = 3 - key.Algorithm = AlgRSASHA256 - key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz" - - fmt.Fprintf(os.Stderr, "%v\n%v\n", sig, soa) - // It should validate. Period is checked seperately, so this will keep on working - if ! Verify(sig, key, []dns.RR{soa}) { - t.Log("Failure to validate") - t.Fail() - } else { - println("It validates!!") - } -} diff --git a/dnssec/ds_test.go b/dnssec/ds_test.go deleted file mode 100644 index 43ef73a5..00000000 --- a/dnssec/ds_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package dnssec - -import ( - "testing" - "strings" - "dns" -) - -func TestKeyToDS(t *testing.T) { - key := new(dns.RR_DNSKEY) - key.Hdr.Name = "miek.nl" - key.Hdr.Rrtype = dns.TypeDNSKEY - key.Hdr.Class = dns.ClassINET - key.Hdr.Ttl = 3600 - key.Flags = 256 - key.Protocol = 3 - key.Algorithm = AlgRSASHA256 - key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz" - - ds := ToDS(key, HashSHA1) - if strings.ToUpper(ds.Digest) != "B5121BDB5B8D86D0CC5FFAFBAAABE26C3E20BAC1" { - t.Logf("Wrong DS digest for Sha1\n%v\n", ds) - t.Fail() - } -} diff --git a/dnssec/keytag_test.go b/dnssec/keytag_test.go deleted file mode 100644 index 10385515..00000000 --- a/dnssec/keytag_test.go +++ /dev/null @@ -1,25 +0,0 @@ -package dnssec - -import ( - "testing" - "dns" -) - -func TestTag(t *testing.T) { - key := new(dns.RR_DNSKEY) - key.Hdr.Name = "miek.nl." - key.Hdr.Rrtype = dns.TypeDNSKEY - key.Hdr.Class = dns.ClassINET - key.Hdr.Ttl = 3600 - key.Flags = 256 - key.Protocol = 3 - key.Algorithm = AlgRSASHA256 - key.PubKey = "AwEAAcNEU67LJI5GEgF9QLNqLO1SMq1EdoQ6E9f85ha0k0ewQGCblyW2836GiVsm6k8Kr5ECIoMJ6fZWf3CQSQ9ycWfTyOHfmI3eQ/1Covhb2y4bAmL/07PhrL7ozWBW3wBfM335Ft9xjtXHPy7ztCbV9qZ4TVDTW/Iyg0PiwgoXVesz" - - tag := KeyTag(key) - if tag != 12051 { - t.Logf("%v\n", key) - t.Logf("Wrong key tag: %d\n", tag) - t.Fail() - } -} diff --git a/dnssec/pack_test.go b/dnssec/pack_test.go deleted file mode 100644 index ae1c3d9b..00000000 --- a/dnssec/pack_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package dnssec - -import ( - "testing" - "dns" -) - -func TestPackUnpack(t *testing.T) { - out := new(dns.Msg) - out.Answer = make([]dns.RR, 1) - key := new(dns.RR_DNSKEY) - key.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeDNSKEY, Class: dns.ClassINET, Ttl: 3600} - key = &dns.RR_DNSKEY{Flags: 257, Protocol: 3, Algorithm: AlgRSASHA1} - key.PubKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ" - - out.Answer[0] = key - msg, ok := out.Pack() - if !ok { - t.Log("Failed to pack msg with DNSKEY") - t.Fail() - } - - in := new(dns.Msg) - if !in.Unpack(msg) { - t.Log("Failed to unpack msg with DNSKEY") - t.Fail() - } - - sig := new(dns.RR_RRSIG) - sig.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeRRSIG, Class: dns.ClassINET, Ttl: 3600} - sig = &dns.RR_RRSIG{TypeCovered: dns.TypeDNSKEY, Algorithm: AlgRSASHA1, Labels: 2, - OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.", - Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"} - - out.Answer[0] = sig - msg, ok = out.Pack() - if !ok { - t.Log("Failed to pack msg with RRSIG") - t.Fail() - } - - if !in.Unpack(msg) { - t.Log("Failed to unpack msg with RRSIG") - t.Fail() - } -} diff --git a/dnssec/signature_test.go b/dnssec/signature_test.go deleted file mode 100644 index 4082f407..00000000 --- a/dnssec/signature_test.go +++ /dev/null @@ -1,35 +0,0 @@ -package dnssec - -import ( - "dns" - "testing" -) - -func TestSignature(t *testing.T) { - sig := new(dns.RR_RRSIG) - sig.Hdr.Name = "miek.nl." - sig.Hdr.Class = dns.ClassINET - sig.Hdr.Ttl = 3600 - sig.TypeCovered = dns.TypeDNSKEY - sig.Algorithm = AlgRSASHA1 - sig.Labels = 2 - sig.OrigTtl = 4000 - sig.Expiration = 1000 //Thu Jan 1 02:06:40 CET 1970 - sig.Inception = 800 //Thu Jan 1 01:13:20 CET 1970 - sig.KeyTag = 34641 - sig.SignerName = "miek.nl." - sig.Signature = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ" - - // Should not be valid - if PeriodOK(sig) { - t.Log("Should not be valid") - t.Fail() - } - - sig.Inception = 315565800 //Tue Jan 1 10:10:00 CET 1980 - sig.Expiration = 4102477800 //Fri Jan 1 10:10:00 CET 2100 - if ! PeriodOK(sig) { - t.Log("Should be valid") - t.Fail() - } -} diff --git a/pack_test.go b/pack_test.go index 6631a305..840cbb4c 100644 --- a/pack_test.go +++ b/pack_test.go @@ -2,48 +2,44 @@ package dns import ( "testing" - "net" ) func TestPackUnpack(t *testing.T) { out := new(Msg) - r := new(RR_AAAA) - r.AAAA = net.ParseIP("2001:7b8:206:1:200:39ff:fe59:b187").To16() - r.Hdr = RR_Header{Name: "a.miek.nl", Rrtype: TypeAAAA, Class: ClassINET, Ttl: 3600} - out.Answer = make([]RR, 1) - out.Answer[0] = r + out.Answer = make([]RR, 1) + key := new(RR_DNSKEY) + key.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeDNSKEY, Class: ClassINET, Ttl: 3600} + key = &RR_DNSKEY{Flags: 257, Protocol: 3, Algorithm: AlgRSASHA1} + key.PubKey = "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ" + out.Answer[0] = key msg, ok := out.Pack() if !ok { - t.Log("Failed to pack msg with AAAA") + t.Log("Failed to pack msg with DNSKEY") t.Fail() } - in := new(Msg) + in := new(Msg) if !in.Unpack(msg) { - t.Log("Failed to unpack msg with AAAA") + t.Log("Failed to unpack msg with DNSKEY") t.Fail() } - edns := new(RR_OPT) - edns.Hdr.Name = "." - edns.Hdr.Rrtype = TypeOPT - edns.Hdr.Class = ClassINET - edns.Hdr.Ttl = 3600 - edns.Option = make([]Option, 1) - edns.SetNsidToString("lalalala") + sig := new(RR_RRSIG) + sig.Hdr = RR_Header{Name: "miek.nl.", Rrtype: TypeRRSIG, Class: ClassINET, Ttl: 3600} + sig = &RR_RRSIG{TypeCovered: TypeDNSKEY, Algorithm: AlgRSASHA1, Labels: 2, + OrigTtl: 3600, Expiration: 4000, Inception: 4000, KeyTag: 34641, SignerName: "miek.nl.", + Signature: "AwEAAaHIwpx3w4VHKi6i1LHnTaWeHCL154Jug0Rtc9ji5qwPXpBo6A5sRv7cSsPQKPIwxLpyCrbJ4mr2L0EPOdvP6z6YfljK2ZmTbogU9aSU2fiq/4wjxbdkLyoDVgtO+JsxNN4bjr4WcWhsmk1Hg93FV9ZpkWb0Tbad8DFqNDzr//kZ"} - _, ok = packRR(edns, msg, 0) + out.Answer[0] = sig + msg, ok = out.Pack() if !ok { - t.Logf("%v\n", edns) - t.Log("Failed") + t.Log("Failed to pack msg with RRSIG") t.Fail() } - unpacked, _, ok := unpackRR(msg, 0) - if !ok { - t.Logf("%v\n", unpacked) - t.Log("Failed") + if !in.Unpack(msg) { + t.Log("Failed to unpack msg with RRSIG") t.Fail() } } diff --git a/resolver/resolver_test.go b/resolver/resolver_test.go index 10203325..2ca0d023 100644 --- a/resolver/resolver_test.go +++ b/resolver/resolver_test.go @@ -22,7 +22,7 @@ func TestResolver(t *testing.T) { ch <- DnsMsg{m, nil} in := <-ch - if in.Dns.Rcode != dns.RcodeSuccess { + if in.Dns != nil && in.Dns.Rcode != dns.RcodeSuccess { t.Log("Failed to get an valid answer") t.Fail() t.Logf("%v\n", in) @@ -33,7 +33,7 @@ func TestResolver(t *testing.T) { ch <- DnsMsg{m, nil} in = <-ch - if in.Dns.Rcode != dns.RcodeSuccess { + if in.Dns != nil && in.Dns.Rcode != dns.RcodeSuccess { t.Log("Failed to get an valid answer") t.Fail() t.Logf("%v\n", in) diff --git a/tsig.go b/tsig.go index 89f185f3..bbdd7d7c 100644 --- a/tsig.go +++ b/tsig.go @@ -1,7 +1,11 @@ package dns +// Implementation of TSIG +// Generation an Validation + import ( "crypto/hmac" + "strconv" "io" ) @@ -11,6 +15,35 @@ const ( HmacSHA1 ) +type RR_TSIG struct { + Hdr RR_Header + Algorithm string "domain-name" + TimeSigned [3]uint16 "TSIG" + Fudge uint16 + MACSize uint16 + MAC string + OrigId uint16 // msg id + Error uint16 + OtherLen uint16 + OtherData string +} + +func (rr *RR_TSIG) Header() *RR_Header { + return &rr.Hdr +} + +func (rr *RR_TSIG) String() string { + // It has no presentation format + return rr.Hdr.String() + + " " + rr.Algorithm + + " " + "" + + " " + strconv.Itoa(int(rr.Fudge)) + + " " + "" + + " " + strconv.Itoa(int(rr.OrigId)) + + " " + strconv.Itoa(int(rr.Error)) + + " " + rr.OtherData +} + // The following values must be put in wireformat, so that // the MAC can be calculated // RFC 2845, section 3.4.2. TSIG Variables @@ -32,34 +65,43 @@ type tsig_generation_fmt struct { // Generate the HMAC for msg. The TSIG RR is modified // to include the MAC and MACSize. Note the the msg Id must // be set, otherwise the MAC is not correct -func (rr *RR_TSIG) GenerateMAC(msg *Msg, secret string) bool { +func (rr *RR_TSIG) Generate(msg *Msg, secret string) bool { buf := make([]byte, 4096) // TODO(mg) bufsize! - tsigbuf := new(tsig_generation_fmt) + tsig := new(tsig_generation_fmt) // Fill the struct and generate the wiredata - tsigbuf.Name = rr.Header().Name - tsigbuf.Class = rr.Header().Class - tsigbuf.Ttl = rr.Header().Ttl - tsigbuf.Algorithm = rr.Algorithm - tsigbuf.TimeSigned = rr.TimeSigned - tsigbuf.Fudge = rr.Fudge - tsigbuf.Error = rr.Error - tsigbuf.OtherLen = rr.OtherLen - tsigbuf.OtherData = rr.OtherData - packStruct(tsigbuf, buf, 0) + tsig.Name = rr.Header().Name + tsig.Class = rr.Header().Class + tsig.Ttl = rr.Header().Ttl + tsig.Algorithm = rr.Algorithm + tsig.TimeSigned = rr.TimeSigned + tsig.Fudge = rr.Fudge + tsig.Error = rr.Error + tsig.OtherLen = rr.OtherLen + tsig.OtherData = rr.OtherData + n, ok1 := packStruct(tsig, buf, 0) + if !ok1 { + return false + } + buf = buf[:n] - msgbuf, ok := msg.Pack() - if !ok { - return false - } - buf = append(buf, msgbuf...) + msgbuf, ok := msg.Pack() + if !ok { + return false + } + buf = append(buf, msgbuf...) - //func NewMD5(key []byte) hash.Hash hmac := hmac.NewMD5([]byte(secret)) io.WriteString(hmac, string(buf)) rr.MAC = string(hmac.Sum()) - rr.MACSize = uint16(len(rr.MAC)) - rr.OrigId = msg.MsgHdr.Id - + rr.MACSize = uint16(len(rr.MAC)) + rr.OrigId = msg.MsgHdr.Id return true } + +// Verify a TSIG. The msg should be the complete message with +// the TSIG record still attached (as the last rr in the Additional +// section) +func (rr *RR_TSIG) Verify(msg *Msg, secret string) bool { + return false +} diff --git a/tsig_test.go b/tsig_test.go index d62081f2..f7fe6d42 100644 --- a/tsig_test.go +++ b/tsig_test.go @@ -7,7 +7,7 @@ import ( func TestTsig(t *testing.T) { tsig := new(RR_TSIG) - tsig.Hdr.Name = "miek.nl" // for tsig this is the key's name + tsig.Hdr.Name = "miek.nl." // for tsig this is the key's name tsig.Hdr.Rrtype = TypeTSIG tsig.Hdr.Class = ClassANY tsig.Hdr.Ttl = 0 @@ -15,9 +15,9 @@ func TestTsig(t *testing.T) { out := new(Msg) out.MsgHdr.RecursionDesired = true out.Question = make([]Question, 1) - out.Question[0] = Question{"miek.nl", TypeSOA, ClassINET} + out.Question[0] = Question{"miek.nl.", TypeSOA, ClassINET} - ok := tsig.GenerateMAC(out, "geheim") + ok := tsig.Generate(out, "geheim") if !ok { t.Log("Failed") t.Fail() diff --git a/types.go b/types.go index d15d99b0..3b055706 100644 --- a/types.go +++ b/types.go @@ -524,30 +524,6 @@ func (rr *RR_TKEY) String() string { return "" } -type RR_TSIG struct { - Hdr RR_Header - Algorithm string "domain-name" - TimeSigned [3]uint16 "TSIG" - Fudge uint16 - MACSize uint16 - MAC string - OrigId uint16 // msg id - Error uint16 - OtherLen uint16 - OtherData string -} - -func (rr *RR_TSIG) Header() *RR_Header { - return &rr.Hdr -} - -func (rr *RR_TSIG) String() string { - // It has no presentation format - return rr.Hdr.String() + - " " + strconv.Itoa(int(rr.MACSize)) + - " " + rr.MAC -} - // Translate the RRSIG's incep. and expir. time to the correct date. // Taking into account serial arithmetic (RFC 1982) func timeToDate(t uint32) string {